kordoc 3.9.0 → 3.10.1
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/README.md +27 -0
- package/dist/{-6CLKMLK2.js → -LPEXY73L.js} +18 -12
- package/dist/{chunk-VHXUD3PW.cjs → chunk-7UXZTZBJ.cjs} +2 -2
- package/dist/{chunk-VHXUD3PW.cjs.map → chunk-7UXZTZBJ.cjs.map} +1 -1
- package/dist/chunk-GFS4QWB6.js +124 -0
- package/dist/chunk-GFS4QWB6.js.map +1 -0
- package/dist/{chunk-EZ5GBTBE.js → chunk-I6CX2RK4.js} +2 -2
- package/dist/{chunk-EZ5GBTBE.js.map → chunk-I6CX2RK4.js.map} +1 -1
- package/dist/{chunk-ITPZD7XK.js → chunk-RMGYE4LT.js} +32 -313
- package/dist/chunk-RMGYE4LT.js.map +1 -0
- package/dist/chunk-UR34PYAC.js +938 -0
- package/dist/chunk-UR34PYAC.js.map +1 -0
- package/dist/{chunk-XKDA4FDM.js → chunk-YXEE3DEQ.js} +4 -120
- package/dist/chunk-YXEE3DEQ.js.map +1 -0
- package/dist/cli.js +33 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +798 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +680 -56
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +8 -5
- package/dist/mcp.js.map +1 -1
- package/dist/{parser-NCNME2Z6.js → parser-664URIIW.js} +2 -2
- package/dist/{parser-N4G5KQBE.cjs → parser-AT22HMF5.cjs} +14 -14
- package/dist/{parser-N4G5KQBE.cjs.map → parser-AT22HMF5.cjs.map} +1 -1
- package/dist/{parser-LVI45MVF.js → parser-LJOS7VZM.js} +8 -6
- package/dist/{parser-LVI45MVF.js.map → parser-LJOS7VZM.js.map} +1 -1
- package/dist/render-XWOEGE3U.js +9 -0
- package/dist/render-XWOEGE3U.js.map +1 -0
- package/dist/{watch-XSLV62BH.js → watch-CZF6NA2V.js} +6 -4
- package/dist/{watch-XSLV62BH.js.map → watch-CZF6NA2V.js.map} +1 -1
- package/package.json +2 -2
- package/dist/chunk-ITPZD7XK.js.map +0 -1
- package/dist/chunk-XKDA4FDM.js.map +0 -1
- /package/dist/{-6CLKMLK2.js.map → -LPEXY73L.js.map} +0 -0
- /package/dist/{parser-NCNME2Z6.js.map → parser-664URIIW.js.map} +0 -0
|
@@ -0,0 +1,938 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
KordocError
|
|
4
|
+
} from "./chunk-GFS4QWB6.js";
|
|
5
|
+
|
|
6
|
+
// src/render/svg-render.ts
|
|
7
|
+
import JSZip from "jszip";
|
|
8
|
+
|
|
9
|
+
// src/hwpx/parser-shared.ts
|
|
10
|
+
import { DOMParser } from "@xmldom/xmldom";
|
|
11
|
+
var MAX_DECOMPRESS_SIZE = 100 * 1024 * 1024;
|
|
12
|
+
var MAX_ZIP_ENTRIES = 500;
|
|
13
|
+
function clampSpan(val, max) {
|
|
14
|
+
return Math.max(1, Math.min(val, max));
|
|
15
|
+
}
|
|
16
|
+
var MAX_XML_DEPTH = 200;
|
|
17
|
+
function createSectionShared() {
|
|
18
|
+
return { numState: /* @__PURE__ */ new Map(), pageText: { headers: [], footers: [] }, track: { deleteDepth: 0, warned: false } };
|
|
19
|
+
}
|
|
20
|
+
function createXmlParser(warnings) {
|
|
21
|
+
return new DOMParser({
|
|
22
|
+
onError(level, msg) {
|
|
23
|
+
if (level === "fatalError") throw new KordocError(`XML \uD30C\uC2F1 \uC2E4\uD328: ${msg}`);
|
|
24
|
+
warnings?.push({ code: "MALFORMED_XML", message: `XML ${level === "warn" ? "\uACBD\uACE0" : "\uC624\uB958"}: ${msg}` });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function applyPageText(blocks, shared) {
|
|
29
|
+
const { headers, footers } = shared.pageText;
|
|
30
|
+
if (headers.length > 0) {
|
|
31
|
+
blocks.unshift(...headers.map((t) => ({ type: "paragraph", text: t, pageNumber: 1 })));
|
|
32
|
+
}
|
|
33
|
+
if (footers.length > 0) {
|
|
34
|
+
blocks.push(...footers.map((t) => ({ type: "paragraph", text: t })));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function findChildByLocalName(parent, name) {
|
|
38
|
+
const children = parent.childNodes;
|
|
39
|
+
if (!children) return null;
|
|
40
|
+
for (let i = 0; i < children.length; i++) {
|
|
41
|
+
const ch = children[i];
|
|
42
|
+
if (ch.nodeType !== 1) continue;
|
|
43
|
+
const tag = (ch.tagName || ch.localName || "").replace(/^[^:]+:/, "");
|
|
44
|
+
if (tag === name) return ch;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function extractTextFromNode(node) {
|
|
49
|
+
let result = "";
|
|
50
|
+
const children = node.childNodes;
|
|
51
|
+
if (!children) return result;
|
|
52
|
+
for (let i = 0; i < children.length; i++) {
|
|
53
|
+
const child = children[i];
|
|
54
|
+
if (child.nodeType === 3) result += child.textContent || "";
|
|
55
|
+
else if (child.nodeType === 1) result += extractTextFromNode(child);
|
|
56
|
+
}
|
|
57
|
+
return result.trim();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/render/layout.ts
|
|
61
|
+
function toInt32(v, fallback = 0) {
|
|
62
|
+
if (v == null || v === "") return fallback;
|
|
63
|
+
const n = Number(v);
|
|
64
|
+
if (!Number.isFinite(n)) return fallback;
|
|
65
|
+
return n > 2147483647 ? n - 4294967296 : n;
|
|
66
|
+
}
|
|
67
|
+
function solveBoundaries(constraints, count, total) {
|
|
68
|
+
const x = new Array(count + 1).fill(void 0);
|
|
69
|
+
x[0] = 0;
|
|
70
|
+
if (total != null && total > 0) x[count] = total;
|
|
71
|
+
let changed = true;
|
|
72
|
+
let guard = 0;
|
|
73
|
+
while (changed && guard++ < count + 8) {
|
|
74
|
+
changed = false;
|
|
75
|
+
for (const c of constraints) {
|
|
76
|
+
if (c.a < 0 || c.b > count || c.a >= c.b) continue;
|
|
77
|
+
const xa = x[c.a];
|
|
78
|
+
const xb = x[c.b];
|
|
79
|
+
if (xa != null && xb == null) {
|
|
80
|
+
x[c.b] = xa + c.size;
|
|
81
|
+
changed = true;
|
|
82
|
+
} else if (xb != null && xa == null) {
|
|
83
|
+
x[c.a] = xb - c.size;
|
|
84
|
+
changed = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let i = 0;
|
|
89
|
+
while (i <= count) {
|
|
90
|
+
if (x[i] != null) {
|
|
91
|
+
i++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
let lo = i - 1;
|
|
95
|
+
let hi = i;
|
|
96
|
+
while (hi <= count && x[hi] == null) hi++;
|
|
97
|
+
const loV = x[lo];
|
|
98
|
+
const hiV = hi <= count ? x[hi] : loV + (hi - lo) * 1e3;
|
|
99
|
+
const n = hi - lo;
|
|
100
|
+
for (let k = 1; k < n; k++) x[lo + k] = loV + (hiV - loV) * k / n;
|
|
101
|
+
if (hi > count) x[count] = hiV;
|
|
102
|
+
i = hi;
|
|
103
|
+
}
|
|
104
|
+
const out = x;
|
|
105
|
+
for (let k = 1; k <= count; k++) if (out[k] < out[k - 1]) out[k] = out[k - 1];
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
function solveRowHeights(cells, rowCount) {
|
|
109
|
+
const h = new Array(rowCount).fill(0);
|
|
110
|
+
for (const c of cells) {
|
|
111
|
+
if (c.rowSpan === 1 && c.rowAddr >= 0 && c.rowAddr < rowCount) {
|
|
112
|
+
h[c.rowAddr] = Math.max(h[c.rowAddr], c.height);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const c of cells) {
|
|
116
|
+
if (c.rowSpan <= 1) continue;
|
|
117
|
+
const rows = [];
|
|
118
|
+
for (let r = c.rowAddr; r < Math.min(c.rowAddr + c.rowSpan, rowCount); r++) rows.push(r);
|
|
119
|
+
const known = rows.reduce((s, r) => s + h[r], 0);
|
|
120
|
+
const missing = rows.filter((r) => h[r] === 0);
|
|
121
|
+
if (missing.length > 0 && c.height > known) {
|
|
122
|
+
const each = (c.height - known) / missing.length;
|
|
123
|
+
for (const r of missing) h[r] = each;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const c of cells) {
|
|
127
|
+
if (c.rowSpan === 1 && c.contentH != null && c.rowAddr >= 0 && c.rowAddr < rowCount) {
|
|
128
|
+
if (c.contentH > h[c.rowAddr]) h[c.rowAddr] = c.contentH;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return h;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/hwpx/text-metrics.ts
|
|
135
|
+
var ASCII_W = [
|
|
136
|
+
300,
|
|
137
|
+
320,
|
|
138
|
+
320,
|
|
139
|
+
610,
|
|
140
|
+
610,
|
|
141
|
+
830,
|
|
142
|
+
724,
|
|
143
|
+
320,
|
|
144
|
+
320,
|
|
145
|
+
320,
|
|
146
|
+
550,
|
|
147
|
+
550,
|
|
148
|
+
320,
|
|
149
|
+
550,
|
|
150
|
+
320,
|
|
151
|
+
550,
|
|
152
|
+
// 0x20-0x2F
|
|
153
|
+
550,
|
|
154
|
+
550,
|
|
155
|
+
550,
|
|
156
|
+
550,
|
|
157
|
+
550,
|
|
158
|
+
550,
|
|
159
|
+
550,
|
|
160
|
+
550,
|
|
161
|
+
550,
|
|
162
|
+
550,
|
|
163
|
+
320,
|
|
164
|
+
320,
|
|
165
|
+
550,
|
|
166
|
+
550,
|
|
167
|
+
550,
|
|
168
|
+
550,
|
|
169
|
+
// 0x30-0x3F
|
|
170
|
+
830,
|
|
171
|
+
706,
|
|
172
|
+
605,
|
|
173
|
+
685,
|
|
174
|
+
719,
|
|
175
|
+
627,
|
|
176
|
+
617,
|
|
177
|
+
683,
|
|
178
|
+
734,
|
|
179
|
+
305,
|
|
180
|
+
315,
|
|
181
|
+
660,
|
|
182
|
+
605,
|
|
183
|
+
839,
|
|
184
|
+
734,
|
|
185
|
+
732,
|
|
186
|
+
// 0x40-0x4F
|
|
187
|
+
603,
|
|
188
|
+
705,
|
|
189
|
+
660,
|
|
190
|
+
627,
|
|
191
|
+
664,
|
|
192
|
+
731,
|
|
193
|
+
706,
|
|
194
|
+
910,
|
|
195
|
+
705,
|
|
196
|
+
705,
|
|
197
|
+
626,
|
|
198
|
+
320,
|
|
199
|
+
550,
|
|
200
|
+
320,
|
|
201
|
+
550,
|
|
202
|
+
550,
|
|
203
|
+
// 0x50-0x5F
|
|
204
|
+
320,
|
|
205
|
+
569,
|
|
206
|
+
597,
|
|
207
|
+
552,
|
|
208
|
+
597,
|
|
209
|
+
536,
|
|
210
|
+
356,
|
|
211
|
+
562,
|
|
212
|
+
635,
|
|
213
|
+
287,
|
|
214
|
+
288,
|
|
215
|
+
582,
|
|
216
|
+
287,
|
|
217
|
+
907,
|
|
218
|
+
635,
|
|
219
|
+
588,
|
|
220
|
+
// 0x60-0x6F
|
|
221
|
+
597,
|
|
222
|
+
579,
|
|
223
|
+
478,
|
|
224
|
+
496,
|
|
225
|
+
356,
|
|
226
|
+
635,
|
|
227
|
+
563,
|
|
228
|
+
720,
|
|
229
|
+
542,
|
|
230
|
+
543,
|
|
231
|
+
486,
|
|
232
|
+
320,
|
|
233
|
+
320,
|
|
234
|
+
320,
|
|
235
|
+
550,
|
|
236
|
+
0
|
|
237
|
+
// 0x70-0x7E(+DEL)
|
|
238
|
+
];
|
|
239
|
+
var SYM_W = {
|
|
240
|
+
160: 300,
|
|
241
|
+
163: 568,
|
|
242
|
+
165: 707,
|
|
243
|
+
167: 498,
|
|
244
|
+
171: 440,
|
|
245
|
+
172: 564,
|
|
246
|
+
176: 291,
|
|
247
|
+
177: 798,
|
|
248
|
+
182: 606,
|
|
249
|
+
183: 320,
|
|
250
|
+
187: 440,
|
|
251
|
+
215: 617,
|
|
252
|
+
247: 678,
|
|
253
|
+
8211: 625,
|
|
254
|
+
8212: 875,
|
|
255
|
+
8213: 875,
|
|
256
|
+
8216: 320,
|
|
257
|
+
8217: 320,
|
|
258
|
+
8220: 480,
|
|
259
|
+
8221: 480,
|
|
260
|
+
8224: 558,
|
|
261
|
+
8225: 438,
|
|
262
|
+
8229: 640,
|
|
263
|
+
8230: 960,
|
|
264
|
+
8240: 988,
|
|
265
|
+
8242: 335,
|
|
266
|
+
8243: 474,
|
|
267
|
+
8251: 770,
|
|
268
|
+
8364: 656,
|
|
269
|
+
9756: 1012,
|
|
270
|
+
9758: 1012
|
|
271
|
+
};
|
|
272
|
+
function charWidthEm1000(cp) {
|
|
273
|
+
if (cp >= 32 && cp <= 126) return ASCII_W[cp - 32];
|
|
274
|
+
const sym = SYM_W[cp];
|
|
275
|
+
if (sym !== void 0) return sym;
|
|
276
|
+
if (cp >= 44032 && cp <= 55203) return 970;
|
|
277
|
+
if (cp >= 4352 && cp <= 4607) return 970;
|
|
278
|
+
if (cp >= 12593 && cp <= 12686) return 970;
|
|
279
|
+
if (cp >= 19968 && cp <= 40959 || cp >= 63744 && cp <= 64255) return 1e3;
|
|
280
|
+
if (cp >= 12296 && cp <= 12305 || cp >= 12308 && cp <= 12315) return 500;
|
|
281
|
+
if (cp === 12288) return 970;
|
|
282
|
+
if (cp >= 8592 && cp <= 8959) return 970;
|
|
283
|
+
if (cp >= 9312 && cp <= 9471) return 970;
|
|
284
|
+
if (cp >= 9632 && cp <= 9983) return 970;
|
|
285
|
+
if (cp >= 12800 && cp <= 13311) return 970;
|
|
286
|
+
if (cp >= 65281 && cp <= 65376) return 970;
|
|
287
|
+
return cp >= 11904 ? 970 : 550;
|
|
288
|
+
}
|
|
289
|
+
var SPACE_EM_FIXED = 500;
|
|
290
|
+
var SPACE_EM_FONT = 300;
|
|
291
|
+
function measureTextWidth(text, height, ratioPct, opts) {
|
|
292
|
+
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
293
|
+
const spacing = opts?.spacingPct ?? 0;
|
|
294
|
+
let em = 0;
|
|
295
|
+
for (const ch of text) {
|
|
296
|
+
const cp = ch.codePointAt(0);
|
|
297
|
+
const w = cp === 32 ? spaceEm : charWidthEm1000(cp);
|
|
298
|
+
em += w * (1 + spacing / 100);
|
|
299
|
+
}
|
|
300
|
+
return em / 1e3 * height * (ratioPct / 100);
|
|
301
|
+
}
|
|
302
|
+
var FORBID_START = /* @__PURE__ */ new Set([..."!%),.:;?]}\xA2\xB0\u2032\u2033\u2103\u3009\u300B\u300D\u300F\u3011\u3015!%),.:;?]}\u20A9~\u2026\xB7\u3001\u3002\u3003"]);
|
|
303
|
+
var FORBID_END = /* @__PURE__ */ new Set([..."$([{\xA3\xA5\u3008\u300A\u300C\u300E\u3010\u3014$([{\u20A9"]);
|
|
304
|
+
function simulateWrap(text, firstWidth, contWidth, height, ratioPct, mode = "keep", opts) {
|
|
305
|
+
const EPS = 0.5;
|
|
306
|
+
const spaceEm = opts?.spaceEm ?? SPACE_EM_FIXED;
|
|
307
|
+
const spacing = opts?.spacingPct ?? 0;
|
|
308
|
+
const k = height * ratioPct / 100 / 1e3;
|
|
309
|
+
const cwCp = (cp) => (cp === 32 ? spaceEm : charWidthEm1000(cp)) * (1 + spacing / 100) * k;
|
|
310
|
+
const charW2 = (ch) => cwCp(ch.codePointAt(0));
|
|
311
|
+
const rangeW = (from, to) => {
|
|
312
|
+
let w = 0;
|
|
313
|
+
for (const ch of text.slice(from, to)) w += charW2(ch);
|
|
314
|
+
return w;
|
|
315
|
+
};
|
|
316
|
+
const units = text.match(mode === "keep" ? / +|[^ ]+/g : / +|[^ ]/g) ?? [];
|
|
317
|
+
const starts = [0];
|
|
318
|
+
let lineW = 0;
|
|
319
|
+
let avail = firstWidth;
|
|
320
|
+
let pos = 0;
|
|
321
|
+
const lineStart = () => starts[starts.length - 1];
|
|
322
|
+
const breakBefore = (unitPos, w) => {
|
|
323
|
+
let bp = unitPos;
|
|
324
|
+
const u = text[unitPos];
|
|
325
|
+
if (u !== void 0 && FORBID_START.has(u) && bp - 1 > lineStart() && text[bp - 1] !== " ") bp--;
|
|
326
|
+
while (bp - 1 > lineStart() && FORBID_END.has(text[bp - 1])) bp--;
|
|
327
|
+
if (bp <= lineStart()) bp = unitPos;
|
|
328
|
+
starts.push(bp);
|
|
329
|
+
avail = contWidth;
|
|
330
|
+
lineW = rangeW(bp, unitPos) + w;
|
|
331
|
+
};
|
|
332
|
+
for (const u of units) {
|
|
333
|
+
if (u[0] === " ") {
|
|
334
|
+
lineW += charW2(" ") * u.length;
|
|
335
|
+
pos += u.length;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const w = rangeW(pos, pos + u.length);
|
|
339
|
+
if (lineW + w <= avail + EPS) {
|
|
340
|
+
lineW += w;
|
|
341
|
+
pos += u.length;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (lineW === 0 || w > contWidth + EPS) {
|
|
345
|
+
let sub = 0;
|
|
346
|
+
for (const ch of u) {
|
|
347
|
+
const c = charW2(ch);
|
|
348
|
+
if (lineW + c > avail + EPS && lineW > 0) breakBefore(pos + sub, 0);
|
|
349
|
+
lineW += c;
|
|
350
|
+
sub += ch.length;
|
|
351
|
+
}
|
|
352
|
+
pos += u.length;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
breakBefore(pos, w);
|
|
356
|
+
pos += u.length;
|
|
357
|
+
}
|
|
358
|
+
return { lines: starts.length, starts, lastLineWidth: lineW };
|
|
359
|
+
}
|
|
360
|
+
function simulateWrapKeepWord(text, firstWidth, contWidth, height, ratioPct, opts) {
|
|
361
|
+
return simulateWrap(text, firstWidth, contWidth, height, ratioPct, "keep", opts);
|
|
362
|
+
}
|
|
363
|
+
function fitRatioForFewerLines(text, firstWidth, contWidth, height, baseRatio, minRatio, opts) {
|
|
364
|
+
const base = simulateWrap(text, firstWidth, contWidth, height, baseRatio, "keep", opts);
|
|
365
|
+
if (base.lines < 2) return null;
|
|
366
|
+
for (let r = baseRatio - 1; r >= minRatio; r--) {
|
|
367
|
+
const sim = simulateWrap(text, firstWidth, contWidth, height, r, "keep", opts);
|
|
368
|
+
if (sim.lines < base.lines) return r;
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// src/render/head-styles.ts
|
|
374
|
+
var DEFAULT_CHAR = { height: 1e3, bold: false, italic: false, underline: false, ratio: 100, spacing: 0 };
|
|
375
|
+
function borderWidthPt(v) {
|
|
376
|
+
const n = parseFloat(v ?? "");
|
|
377
|
+
if (!Number.isFinite(n)) return 0.34;
|
|
378
|
+
return n * 2.834645;
|
|
379
|
+
}
|
|
380
|
+
function parseEdge(el) {
|
|
381
|
+
if (!el) return void 0;
|
|
382
|
+
const type = el.getAttribute("type") ?? "NONE";
|
|
383
|
+
if (type === "NONE") return void 0;
|
|
384
|
+
return { type, widthPt: borderWidthPt(el.getAttribute("width")), color: el.getAttribute("color") ?? "#000000" };
|
|
385
|
+
}
|
|
386
|
+
function parseRenderStyles(headXml) {
|
|
387
|
+
const styles = { charPr: /* @__PURE__ */ new Map(), paraAlign: /* @__PURE__ */ new Map(), borderFill: /* @__PURE__ */ new Map() };
|
|
388
|
+
const doc = createXmlParser().parseFromString(headXml, "text/xml");
|
|
389
|
+
const root = doc.documentElement;
|
|
390
|
+
if (!root) return styles;
|
|
391
|
+
const walk = (el) => {
|
|
392
|
+
const tag = (el.tagName || "").replace(/^[^:]+:/, "");
|
|
393
|
+
if (tag === "charPr") {
|
|
394
|
+
const id = el.getAttribute("id");
|
|
395
|
+
if (id != null) {
|
|
396
|
+
const ratioEl = findChildByLocalName(el, "ratio");
|
|
397
|
+
const spacingEl = findChildByLocalName(el, "spacing");
|
|
398
|
+
const underlineEl = findChildByLocalName(el, "underline");
|
|
399
|
+
const textColor = el.getAttribute("textColor");
|
|
400
|
+
styles.charPr.set(id, {
|
|
401
|
+
height: Number(el.getAttribute("height")) || 1e3,
|
|
402
|
+
bold: findChildByLocalName(el, "bold") != null,
|
|
403
|
+
italic: findChildByLocalName(el, "italic") != null,
|
|
404
|
+
underline: underlineEl != null && (underlineEl.getAttribute("type") ?? "NONE") !== "NONE",
|
|
405
|
+
color: textColor && textColor !== "#000000" && textColor.toLowerCase() !== "none" ? textColor : void 0,
|
|
406
|
+
ratio: Number(ratioEl?.getAttribute("hangul")) || 100,
|
|
407
|
+
spacing: Number(spacingEl?.getAttribute("hangul")) || 0
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
} else if (tag === "paraPr") {
|
|
411
|
+
const id = el.getAttribute("id");
|
|
412
|
+
if (id != null) {
|
|
413
|
+
const align = findChildByLocalName(el, "align");
|
|
414
|
+
styles.paraAlign.set(id, align?.getAttribute("horizontal") || "JUSTIFY");
|
|
415
|
+
}
|
|
416
|
+
} else if (tag === "borderFill") {
|
|
417
|
+
const id = el.getAttribute("id");
|
|
418
|
+
if (id != null) {
|
|
419
|
+
const bf = {
|
|
420
|
+
left: parseEdge(findChildByLocalName(el, "leftBorder")),
|
|
421
|
+
right: parseEdge(findChildByLocalName(el, "rightBorder")),
|
|
422
|
+
top: parseEdge(findChildByLocalName(el, "topBorder")),
|
|
423
|
+
bottom: parseEdge(findChildByLocalName(el, "bottomBorder"))
|
|
424
|
+
};
|
|
425
|
+
const fillBrush = findChildByLocalName(el, "fillBrush");
|
|
426
|
+
const winBrush = fillBrush ? findChildByLocalName(fillBrush, "winBrush") : null;
|
|
427
|
+
const face = winBrush?.getAttribute("faceColor");
|
|
428
|
+
if (face && face.toLowerCase() !== "none") bf.fill = face;
|
|
429
|
+
styles.borderFill.set(id, bf);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
const children = el.childNodes;
|
|
433
|
+
for (let i = 0; i < children.length; i++) {
|
|
434
|
+
const ch = children[i];
|
|
435
|
+
if (ch.nodeType === 1) walk(ch);
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
walk(root);
|
|
439
|
+
return styles;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/render/svg-render.ts
|
|
443
|
+
function ln(el) {
|
|
444
|
+
return (el.tagName || "").replace(/^[^:]+:/, "");
|
|
445
|
+
}
|
|
446
|
+
function elements(el) {
|
|
447
|
+
const out = [];
|
|
448
|
+
const children = el.childNodes;
|
|
449
|
+
if (!children) return out;
|
|
450
|
+
for (let i = 0; i < children.length; i++) {
|
|
451
|
+
if (children[i].nodeType === 1) out.push(children[i]);
|
|
452
|
+
}
|
|
453
|
+
return out;
|
|
454
|
+
}
|
|
455
|
+
function num(el, attr, fallback = 0) {
|
|
456
|
+
return el ? toInt32(el.getAttribute(attr) ?? void 0, fallback) : fallback;
|
|
457
|
+
}
|
|
458
|
+
function findFirst(el, name, depth = 0) {
|
|
459
|
+
if (depth > 64) return null;
|
|
460
|
+
for (const ch of elements(el)) {
|
|
461
|
+
if (ln(ch) === name) return ch;
|
|
462
|
+
const found = findFirst(ch, name, depth + 1);
|
|
463
|
+
if (found) return found;
|
|
464
|
+
}
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
function escapeXml(s) {
|
|
468
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
469
|
+
}
|
|
470
|
+
var OBJ_TAGS = /* @__PURE__ */ new Set(["tbl", "pic", "container", "equation", "rect", "ellipse", "polygon", "curv", "line", "arc", "ole", "textart"]);
|
|
471
|
+
var pt = (u) => String(Math.round(u) / 100);
|
|
472
|
+
function warnOnce(ctx, key, msg) {
|
|
473
|
+
if (ctx.warned.has(key)) return;
|
|
474
|
+
ctx.warned.add(key);
|
|
475
|
+
ctx.warnings.push(msg);
|
|
476
|
+
}
|
|
477
|
+
function textOfT(t) {
|
|
478
|
+
let s = "";
|
|
479
|
+
const walk = (n, d) => {
|
|
480
|
+
if (d > 32) return;
|
|
481
|
+
const kids = n.childNodes;
|
|
482
|
+
if (!kids) return;
|
|
483
|
+
for (let i = 0; i < kids.length; i++) {
|
|
484
|
+
const c = kids[i];
|
|
485
|
+
if (c.nodeType === 3) s += c.textContent ?? "";
|
|
486
|
+
else if (c.nodeType === 1) walk(c, d + 1);
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
walk(t, 0);
|
|
490
|
+
return s;
|
|
491
|
+
}
|
|
492
|
+
function buildPara(p) {
|
|
493
|
+
const chars = [];
|
|
494
|
+
const objs = [];
|
|
495
|
+
let segs = [];
|
|
496
|
+
for (const runEl of elements(p)) {
|
|
497
|
+
const tag = ln(runEl);
|
|
498
|
+
if (tag === "run") {
|
|
499
|
+
const prId = runEl.getAttribute("charPrIDRef");
|
|
500
|
+
for (const ch of elements(runEl)) {
|
|
501
|
+
const cn = ln(ch);
|
|
502
|
+
if (cn === "t") {
|
|
503
|
+
for (const c of textOfT(ch)) chars.push({ ch: c, prId });
|
|
504
|
+
} else if (OBJ_TAGS.has(cn)) {
|
|
505
|
+
const sz = findChildByLocalName(ch, "sz");
|
|
506
|
+
const pos = findChildByLocalName(ch, "pos");
|
|
507
|
+
objs.push({
|
|
508
|
+
el: ch,
|
|
509
|
+
tag: cn,
|
|
510
|
+
index: chars.length,
|
|
511
|
+
inline: pos?.getAttribute("treatAsChar") === "1",
|
|
512
|
+
width: num(sz, "width"),
|
|
513
|
+
height: num(sz, "height")
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
} else if (tag === "linesegarray") {
|
|
518
|
+
segs = elements(runEl).filter((s) => ln(s) === "lineseg").map((s) => ({
|
|
519
|
+
textpos: num(s, "textpos"),
|
|
520
|
+
vertpos: num(s, "vertpos"),
|
|
521
|
+
horzpos: num(s, "horzpos"),
|
|
522
|
+
horzsize: num(s, "horzsize"),
|
|
523
|
+
textheight: num(s, "textheight", 1e3),
|
|
524
|
+
baseline: num(s, "baseline", 850)
|
|
525
|
+
}));
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return { chars, segs, objs, paraPrId: p.getAttribute("paraPrIDRef") };
|
|
529
|
+
}
|
|
530
|
+
function charW(c, styles) {
|
|
531
|
+
const st = (c.prId != null ? styles.charPr.get(c.prId) : void 0) ?? DEFAULT_CHAR;
|
|
532
|
+
return measureTextWidth(c.ch, st.height, st.ratio, { spacingPct: st.spacing });
|
|
533
|
+
}
|
|
534
|
+
function lineNaturalWidth(m, styles, start, end) {
|
|
535
|
+
let text = 0;
|
|
536
|
+
for (let i = start; i < end && i < m.chars.length; i++) text += charW(m.chars[i], styles);
|
|
537
|
+
let obj = 0;
|
|
538
|
+
for (const o of m.objs) if (o.inline && o.index >= start && o.index < end) obj += o.width;
|
|
539
|
+
return { text, obj };
|
|
540
|
+
}
|
|
541
|
+
function planLines(m, styles) {
|
|
542
|
+
const align = (m.paraPrId != null ? styles.paraAlign.get(m.paraPrId) : void 0) ?? "JUSTIFY";
|
|
543
|
+
const plans = [];
|
|
544
|
+
for (let i = 0; i < m.segs.length; i++) {
|
|
545
|
+
const seg = m.segs[i];
|
|
546
|
+
const start = seg.textpos;
|
|
547
|
+
const end = i + 1 < m.segs.length ? m.segs[i + 1].textpos : Math.max(m.chars.length, start);
|
|
548
|
+
const nat = lineNaturalWidth(m, styles, start, end);
|
|
549
|
+
const isLast = i === m.segs.length - 1;
|
|
550
|
+
let xoff = 0;
|
|
551
|
+
let scale = 1;
|
|
552
|
+
const avail = seg.horzsize - nat.obj;
|
|
553
|
+
if (nat.text > 0 && (!isLast || align === "DISTRIBUTE" || align === "DISTRIBUTE_SPACE")) {
|
|
554
|
+
scale = avail > 0 ? avail / nat.text : 1;
|
|
555
|
+
} else if (nat.text + nat.obj > 0 && isLast) {
|
|
556
|
+
const w = nat.text + nat.obj;
|
|
557
|
+
if (align === "CENTER") xoff = Math.max(0, (seg.horzsize - w) / 2);
|
|
558
|
+
else if (align === "RIGHT") xoff = Math.max(0, seg.horzsize - w);
|
|
559
|
+
}
|
|
560
|
+
if (!Number.isFinite(scale) || scale <= 0) scale = 1;
|
|
561
|
+
scale = Math.min(4, Math.max(0.25, scale));
|
|
562
|
+
plans.push({ seg, xoff, scale, start, end });
|
|
563
|
+
}
|
|
564
|
+
return plans;
|
|
565
|
+
}
|
|
566
|
+
function advanceTo(m, styles, plan, upto) {
|
|
567
|
+
let x = 0;
|
|
568
|
+
for (let i = plan.start; i < upto && i < m.chars.length; i++) x += charW(m.chars[i], styles) * plan.scale;
|
|
569
|
+
for (const o of m.objs) if (o.inline && o.index >= plan.start && o.index < upto) x += o.width;
|
|
570
|
+
return x;
|
|
571
|
+
}
|
|
572
|
+
function drawPara(p, ox, oy, areaW, ctx, depth) {
|
|
573
|
+
if (depth > 16) {
|
|
574
|
+
warnOnce(ctx, "depth", "\uC911\uCCA9 \uAE4A\uC774 16 \uCD08\uACFC \u2014 \uC774\uD558 \uC0DD\uB7B5");
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const m = buildPara(p);
|
|
578
|
+
if (m.segs.length === 0) {
|
|
579
|
+
for (const o of m.objs) drawObject(o, ox, oy, 0, areaW, ctx, depth);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const plans = planLines(m, ctx.styles);
|
|
583
|
+
const baseV = m.segs[0].vertpos;
|
|
584
|
+
for (const plan of plans) {
|
|
585
|
+
const { seg } = plan;
|
|
586
|
+
let i = plan.start;
|
|
587
|
+
let cursor = ox + seg.horzpos + plan.xoff;
|
|
588
|
+
const y = oy + seg.vertpos + seg.baseline;
|
|
589
|
+
while (i < plan.end && i < m.chars.length) {
|
|
590
|
+
const prId = m.chars[i].prId;
|
|
591
|
+
let j = i;
|
|
592
|
+
let piece = "";
|
|
593
|
+
while (j < plan.end && j < m.chars.length && m.chars[j].prId === prId) {
|
|
594
|
+
piece += m.chars[j].ch;
|
|
595
|
+
j++;
|
|
596
|
+
}
|
|
597
|
+
for (const o of m.objs) {
|
|
598
|
+
if (o.inline && o.index > i && o.index < j) {
|
|
599
|
+
piece = piece.slice(0, o.index - i);
|
|
600
|
+
j = o.index;
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
{
|
|
605
|
+
const cut = piece.search(/ {2,}/);
|
|
606
|
+
if (cut > 0) {
|
|
607
|
+
piece = piece.slice(0, cut);
|
|
608
|
+
j = i + cut;
|
|
609
|
+
} else if (cut === 0) {
|
|
610
|
+
const runEnd = piece.match(/^ +/)[0].length;
|
|
611
|
+
piece = piece.slice(0, runEnd);
|
|
612
|
+
j = i + runEnd;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
const st = (prId != null ? ctx.styles.charPr.get(prId) : void 0) ?? DEFAULT_CHAR;
|
|
616
|
+
const w = measureTextWidth(piece, st.height, st.ratio, { spacingPct: st.spacing }) * plan.scale;
|
|
617
|
+
if (piece.trim().length > 0) {
|
|
618
|
+
const attrs = [`x="${pt(cursor)}"`, `y="${pt(y)}"`, `font-size="${pt(st.height)}"`];
|
|
619
|
+
if ([...piece].length > 1 && w > 50) {
|
|
620
|
+
attrs.push(`textLength="${pt(w)}"`, `lengthAdjust="${plan.scale < 1 ? "spacingAndGlyphs" : "spacing"}"`);
|
|
621
|
+
}
|
|
622
|
+
if (st.bold) attrs.push(`font-weight="bold"`);
|
|
623
|
+
if (st.italic) attrs.push(`font-style="italic"`);
|
|
624
|
+
if (st.underline) attrs.push(`text-decoration="underline"`);
|
|
625
|
+
if (st.color) attrs.push(`fill="${escapeXml(st.color)}"`);
|
|
626
|
+
ctx.svg.push(`<text ${attrs.join(" ")}>${escapeXml(piece)}</text>`);
|
|
627
|
+
ctx.stats.texts++;
|
|
628
|
+
}
|
|
629
|
+
cursor += w;
|
|
630
|
+
for (const o of m.objs) if (o.inline && o.index === j) cursor += o.width;
|
|
631
|
+
i = j;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
for (const o of m.objs) {
|
|
635
|
+
if (o.inline) {
|
|
636
|
+
let plan = plans[0];
|
|
637
|
+
for (const pl of plans) if (pl.start <= o.index && (o.index < pl.end || pl === plans[plans.length - 1])) plan = pl;
|
|
638
|
+
const x = ox + plan.seg.horzpos + plan.xoff + advanceTo(m, ctx.styles, plan, o.index);
|
|
639
|
+
const yTop = oy + plan.seg.vertpos + Math.max(0, plan.seg.baseline - o.height);
|
|
640
|
+
drawObject(o, x, yTop, baseV, areaW, ctx, depth);
|
|
641
|
+
} else {
|
|
642
|
+
const { x, y } = anchorObject(o, ox, oy, baseV, areaW, ctx);
|
|
643
|
+
drawObject(o, x, y, baseV, areaW, ctx, depth);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
function anchorObject(o, ox, oy, baseV, areaW, ctx) {
|
|
648
|
+
const { PW, PH, ML, MT, BODY_W, BODY_H } = ctx.geom;
|
|
649
|
+
const pos = findChildByLocalName(o.el, "pos");
|
|
650
|
+
const om = findChildByLocalName(o.el, "outMargin");
|
|
651
|
+
const omT = num(om, "top"), omB = num(om, "bottom");
|
|
652
|
+
const w = o.width, h = o.height;
|
|
653
|
+
if (!pos) return { x: ox, y: oy + baseV };
|
|
654
|
+
const vo = num(pos, "vertOffset");
|
|
655
|
+
const ho = num(pos, "horzOffset");
|
|
656
|
+
const vrel = pos.getAttribute("vertRelTo") ?? "PARA";
|
|
657
|
+
const hrel = pos.getAttribute("horzRelTo") ?? "PARA";
|
|
658
|
+
const va = pos.getAttribute("vertAlign") ?? "TOP";
|
|
659
|
+
const ha = pos.getAttribute("horzAlign") ?? "LEFT";
|
|
660
|
+
const wrap = o.el.getAttribute("textWrap") ?? "TOP_AND_BOTTOM";
|
|
661
|
+
let y;
|
|
662
|
+
if (vrel === "PAPER") {
|
|
663
|
+
y = va === "BOTTOM" ? PH - h - vo : va === "CENTER" ? (PH - h) / 2 + vo : vo;
|
|
664
|
+
} else if (vrel === "PAGE") {
|
|
665
|
+
y = va === "BOTTOM" ? MT + BODY_H - h - vo : va === "CENTER" ? MT + (BODY_H - h) / 2 + vo : MT + vo;
|
|
666
|
+
} else if (wrap === "TOP_AND_BOTTOM") {
|
|
667
|
+
const pushed = baseV - (omT + h + omB);
|
|
668
|
+
const anchor = pushed >= -100 ? pushed : baseV;
|
|
669
|
+
y = oy + anchor + omT + vo;
|
|
670
|
+
} else {
|
|
671
|
+
y = oy + baseV + vo;
|
|
672
|
+
}
|
|
673
|
+
let x;
|
|
674
|
+
if (hrel === "PAGE") {
|
|
675
|
+
x = ha === "RIGHT" ? ML + BODY_W - w - ho : ha === "CENTER" ? ML + (BODY_W - w) / 2 + ho : ML + ho;
|
|
676
|
+
} else if (hrel === "PAPER") {
|
|
677
|
+
x = ha === "RIGHT" ? PW - w - ho : ha === "CENTER" ? (PW - w) / 2 + ho : ho;
|
|
678
|
+
} else {
|
|
679
|
+
x = ha === "RIGHT" ? ox + areaW - w - ho : ha === "CENTER" ? ox + (areaW - w) / 2 + ho : ox + ho;
|
|
680
|
+
}
|
|
681
|
+
return { x, y };
|
|
682
|
+
}
|
|
683
|
+
function drawObject(o, x, y, baseV, areaW, ctx, depth) {
|
|
684
|
+
if (o.tag === "tbl") drawTable(o.el, x, y, ctx, depth + 1);
|
|
685
|
+
else if (o.tag === "pic") drawPic(o.el, x, y, ctx);
|
|
686
|
+
else if (o.tag === "container") {
|
|
687
|
+
for (const ch of elements(o.el)) {
|
|
688
|
+
const tag = ln(ch);
|
|
689
|
+
if (!OBJ_TAGS.has(tag)) continue;
|
|
690
|
+
const sz = findChildByLocalName(ch, "sz");
|
|
691
|
+
const off = findChildByLocalName(ch, "offset");
|
|
692
|
+
const sub = { el: ch, tag, index: 0, inline: true, width: num(sz, "width"), height: num(sz, "height") };
|
|
693
|
+
drawObject(sub, x + num(off, "x"), y + num(off, "y"), baseV, areaW, ctx, depth + 1);
|
|
694
|
+
}
|
|
695
|
+
} else if (o.tag === "equation") {
|
|
696
|
+
warnOnce(ctx, "equation", "\uC218\uC2DD \uAC1C\uCCB4\uB294 \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5");
|
|
697
|
+
} else {
|
|
698
|
+
warnOnce(ctx, `shape:${o.tag}`, `\uB3C4\uD615 \uAC1C\uCCB4(${o.tag}) \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5`);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
function cellContentExtent(cell, ctx) {
|
|
702
|
+
if (!cell.sub) return 0;
|
|
703
|
+
let ext = 0;
|
|
704
|
+
for (const p of elements(cell.sub)) {
|
|
705
|
+
if (ln(p) !== "p") continue;
|
|
706
|
+
const m = buildPara(p);
|
|
707
|
+
for (const s of m.segs) ext = Math.max(ext, s.vertpos + s.textheight);
|
|
708
|
+
const baseV = m.segs[0]?.vertpos ?? 0;
|
|
709
|
+
for (const o of m.objs) {
|
|
710
|
+
if (o.inline) continue;
|
|
711
|
+
const pos = findChildByLocalName(o.el, "pos");
|
|
712
|
+
if ((pos?.getAttribute("vertRelTo") ?? "PARA") !== "PARA") continue;
|
|
713
|
+
const om = findChildByLocalName(o.el, "outMargin");
|
|
714
|
+
const pushed = baseV - (num(om, "top") + o.height + num(om, "bottom"));
|
|
715
|
+
const anchor = pushed >= -100 ? pushed : baseV;
|
|
716
|
+
ext = Math.max(ext, anchor + num(om, "top") + num(pos, "vertOffset") + o.height);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return ext;
|
|
720
|
+
}
|
|
721
|
+
function edgeLine(x1, y1, x2, y2, e) {
|
|
722
|
+
const dash = /DASH|DOT/.test(e.type) ? ` stroke-dasharray="${e.type.includes("DOT") ? "1,1.5" : "3,1.5"}"` : "";
|
|
723
|
+
return `<line x1="${pt(x1)}" y1="${pt(y1)}" x2="${pt(x2)}" y2="${pt(y2)}" stroke="${escapeXml(e.color)}" stroke-width="${e.widthPt.toFixed(2)}"${dash}/>`;
|
|
724
|
+
}
|
|
725
|
+
function drawTable(tbl, tx, ty, ctx, depth) {
|
|
726
|
+
if (depth > 16) {
|
|
727
|
+
warnOnce(ctx, "depth", "\uC911\uCCA9 \uAE4A\uC774 16 \uCD08\uACFC \u2014 \uC774\uD558 \uC0DD\uB7B5");
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
ctx.stats.tables++;
|
|
731
|
+
const tblSz = findChildByLocalName(tbl, "sz");
|
|
732
|
+
const inMargin = findChildByLocalName(tbl, "inMargin");
|
|
733
|
+
const defL = num(inMargin, "left", 141), defR = num(inMargin, "right", 141);
|
|
734
|
+
const defT = num(inMargin, "top", 141), defB = num(inMargin, "bottom", 141);
|
|
735
|
+
const cells = [];
|
|
736
|
+
for (const tr of elements(tbl)) {
|
|
737
|
+
if (ln(tr) !== "tr") continue;
|
|
738
|
+
for (const tc of elements(tr)) {
|
|
739
|
+
if (ln(tc) !== "tc") continue;
|
|
740
|
+
const addr = findChildByLocalName(tc, "cellAddr");
|
|
741
|
+
const span = findChildByLocalName(tc, "cellSpan");
|
|
742
|
+
const csz = findChildByLocalName(tc, "cellSz");
|
|
743
|
+
const cm = findChildByLocalName(tc, "cellMargin");
|
|
744
|
+
if (!addr || !csz) continue;
|
|
745
|
+
cells.push({
|
|
746
|
+
el: tc,
|
|
747
|
+
ca: num(addr, "colAddr"),
|
|
748
|
+
ra: num(addr, "rowAddr"),
|
|
749
|
+
cs: Math.max(1, num(span, "colSpan", 1)),
|
|
750
|
+
rs: Math.max(1, num(span, "rowSpan", 1)),
|
|
751
|
+
w: num(csz, "width"),
|
|
752
|
+
h: num(csz, "height"),
|
|
753
|
+
bfId: tc.getAttribute("borderFillIDRef"),
|
|
754
|
+
sub: findChildByLocalName(tc, "subList"),
|
|
755
|
+
marginL: cm ? num(cm, "left", defL) : defL,
|
|
756
|
+
marginR: cm ? num(cm, "right", defR) : defR,
|
|
757
|
+
marginT: cm ? num(cm, "top", defT) : defT,
|
|
758
|
+
marginB: cm ? num(cm, "bottom", defB) : defB
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
if (cells.length === 0 || cells.length > 4096) return;
|
|
763
|
+
const nCols = Math.max(...cells.map((c) => c.ca + c.cs));
|
|
764
|
+
const nRows = Math.max(...cells.map((c) => c.ra + c.rs));
|
|
765
|
+
const colCons = cells.map((c) => ({ a: c.ca, b: c.ca + c.cs, size: c.w }));
|
|
766
|
+
const colX = solveBoundaries(colCons, nCols, num(tblSz, "width") || void 0);
|
|
767
|
+
const rowH = solveRowHeights(
|
|
768
|
+
cells.map((c) => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c, ctx) : void 0 })),
|
|
769
|
+
nRows
|
|
770
|
+
);
|
|
771
|
+
const rowY = [0];
|
|
772
|
+
for (let r = 0; r < nRows; r++) rowY.push(rowY[r] + rowH[r]);
|
|
773
|
+
const geom = cells.map((c) => ({
|
|
774
|
+
c,
|
|
775
|
+
x: tx + colX[c.ca],
|
|
776
|
+
y: ty + rowY[c.ra],
|
|
777
|
+
w: colX[Math.min(c.ca + c.cs, nCols)] - colX[c.ca],
|
|
778
|
+
h: rowY[Math.min(c.ra + c.rs, nRows)] - rowY[c.ra]
|
|
779
|
+
}));
|
|
780
|
+
for (const g of geom) {
|
|
781
|
+
const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : void 0;
|
|
782
|
+
if (bf?.fill) ctx.svg.push(`<rect x="${pt(g.x)}" y="${pt(g.y)}" width="${pt(g.w)}" height="${pt(g.h)}" fill="${escapeXml(bf.fill)}"/>`);
|
|
783
|
+
}
|
|
784
|
+
for (const g of geom) {
|
|
785
|
+
const { c } = g;
|
|
786
|
+
if (!c.sub) continue;
|
|
787
|
+
const innerH = g.h - c.marginT - c.marginB;
|
|
788
|
+
const extent = cellContentExtent(c, ctx);
|
|
789
|
+
const va = c.sub.getAttribute("vertAlign") ?? "TOP";
|
|
790
|
+
let yoff = 0;
|
|
791
|
+
if (va === "CENTER") yoff = Math.max(0, (innerH - extent) / 2);
|
|
792
|
+
else if (va === "BOTTOM") yoff = Math.max(0, innerH - extent);
|
|
793
|
+
for (const p of elements(c.sub)) {
|
|
794
|
+
if (ln(p) !== "p") continue;
|
|
795
|
+
drawPara(p, g.x + c.marginL, g.y + c.marginT + yoff, g.w - c.marginL - c.marginR, ctx, depth + 1);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
for (const g of geom) {
|
|
799
|
+
const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : void 0;
|
|
800
|
+
if (!bf) continue;
|
|
801
|
+
if (bf.top) ctx.svg.push(edgeLine(g.x, g.y, g.x + g.w, g.y, bf.top));
|
|
802
|
+
if (bf.bottom) ctx.svg.push(edgeLine(g.x, g.y + g.h, g.x + g.w, g.y + g.h, bf.bottom));
|
|
803
|
+
if (bf.left) ctx.svg.push(edgeLine(g.x, g.y, g.x, g.y + g.h, bf.left));
|
|
804
|
+
if (bf.right) ctx.svg.push(edgeLine(g.x + g.w, g.y, g.x + g.w, g.y + g.h, bf.right));
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
function drawPic(pic, x, y, ctx) {
|
|
808
|
+
const sz = findChildByLocalName(pic, "sz");
|
|
809
|
+
const w = num(sz, "width", 5669), h = num(sz, "height", 5669);
|
|
810
|
+
const img = findFirst(pic, "img");
|
|
811
|
+
const ref = img?.getAttribute("binaryItemIDRef");
|
|
812
|
+
const loaded = ref != null ? ctx.images.get(ref) : void 0;
|
|
813
|
+
if (!loaded) {
|
|
814
|
+
ctx.svg.push(`<rect x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" fill="#eee" stroke="#c00" stroke-width="0.5"/>`);
|
|
815
|
+
warnOnce(ctx, `img:${ref}`, `\uC774\uBBF8\uC9C0 \uBC14\uC774\uB108\uB9AC \uB204\uB77D: ${ref ?? "(ref \uC5C6\uC74C)"}`);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
ctx.stats.images++;
|
|
819
|
+
const orgSz = findChildByLocalName(pic, "orgSz");
|
|
820
|
+
const clip = findChildByLocalName(pic, "imgClip");
|
|
821
|
+
const orgW = num(orgSz, "width"), orgH = num(orgSz, "height");
|
|
822
|
+
const cl = num(clip, "left"), ct = num(clip, "top");
|
|
823
|
+
const cr = num(clip, "right", orgW), cb = num(clip, "bottom", orgH);
|
|
824
|
+
const cropped = orgW > 0 && orgH > 0 && clip != null && (cl > 0 || ct > 0 || cr < orgW || cb < orgH) && cr > cl && cb > ct;
|
|
825
|
+
if (cropped) {
|
|
826
|
+
ctx.svg.push(
|
|
827
|
+
`<svg x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" viewBox="${pt(cl)} ${pt(ct)} ${pt(cr - cl)} ${pt(cb - ct)}" preserveAspectRatio="none"><image x="0" y="0" width="${pt(orgW)}" height="${pt(orgH)}" preserveAspectRatio="none" href="${loaded.dataUri}"/></svg>`
|
|
828
|
+
);
|
|
829
|
+
} else {
|
|
830
|
+
ctx.svg.push(`<image x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" preserveAspectRatio="none" href="${loaded.dataUri}"/>`);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
function sniffMime(name, bytes) {
|
|
834
|
+
const lower = name.toLowerCase();
|
|
835
|
+
if (lower.endsWith(".png") || bytes.length > 4 && bytes[0] === 137 && bytes[1] === 80) return "image/png";
|
|
836
|
+
if (lower.endsWith(".bmp") || bytes.length > 2 && bytes[0] === 66 && bytes[1] === 77) return "image/bmp";
|
|
837
|
+
if (lower.endsWith(".gif")) return "image/gif";
|
|
838
|
+
if (lower.endsWith(".svg")) return "image/svg+xml";
|
|
839
|
+
return "image/jpeg";
|
|
840
|
+
}
|
|
841
|
+
async function renderHwpxToSvg(input, options) {
|
|
842
|
+
const maxImg = options?.maxImageBytes ?? 40 * 1024 * 1024;
|
|
843
|
+
let zip;
|
|
844
|
+
try {
|
|
845
|
+
zip = await JSZip.loadAsync(input);
|
|
846
|
+
} catch {
|
|
847
|
+
throw new KordocError("HWPX(ZIP) \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4 \u2014 \uB80C\uB354\uB294 HWPX\uB9CC \uC9C0\uC6D0");
|
|
848
|
+
}
|
|
849
|
+
const secFile = zip.file("Contents/section0.xml") ?? zip.file(/Contents\/section\d+\.xml$/i).sort((a, b) => a.name.localeCompare(b.name))[0];
|
|
850
|
+
if (!secFile) throw new KordocError("Contents/section0.xml \uC5C6\uC74C \u2014 HWPX\uAC00 \uC544\uB2C8\uAC70\uB098 \uC190\uC0C1\uB428");
|
|
851
|
+
const secXml = await secFile.async("string");
|
|
852
|
+
if (secXml.length > MAX_DECOMPRESS_SIZE) throw new KordocError("\uC139\uC158 XML\uC774 \uD5C8\uC6A9 \uD06C\uAE30\uB97C \uCD08\uACFC");
|
|
853
|
+
if (!secXml.includes("linesegarray")) {
|
|
854
|
+
throw new KordocError("\uC870\uD310 \uCE90\uC2DC(linesegarray) \uC5C6\uC74C \u2014 \uD55C\uCEF4\uC5D0\uC11C \uC800\uC7A5\uD55C HWPX\uB9CC \uB80C\uB354 \uAC00\uB2A5");
|
|
855
|
+
}
|
|
856
|
+
const warnings = [];
|
|
857
|
+
const headFile = zip.file("Contents/header.xml") ?? zip.file("Contents/head.xml");
|
|
858
|
+
const styles = headFile ? parseRenderStyles(await headFile.async("string")) : { charPr: /* @__PURE__ */ new Map(), paraAlign: /* @__PURE__ */ new Map(), borderFill: /* @__PURE__ */ new Map() };
|
|
859
|
+
if (!headFile) warnings.push("header.xml \uC5C6\uC74C \u2014 \uAE30\uBCF8 \uC2A4\uD0C0\uC77C\uB85C \uB80C\uB354");
|
|
860
|
+
const binmap = /* @__PURE__ */ new Map();
|
|
861
|
+
const hpf = zip.file(/content\.hpf$/i)[0];
|
|
862
|
+
if (hpf) {
|
|
863
|
+
const man = await hpf.async("string");
|
|
864
|
+
for (const m of man.matchAll(/<[^>]*\bid="([^"]+)"[^>]*\bhref="(BinData\/[^"]+)"[^>]*>/g)) binmap.set(m[1], m[2]);
|
|
865
|
+
for (const m of man.matchAll(/<[^>]*\bhref="(BinData\/[^"]+)"[^>]*\bid="([^"]+)"[^>]*>/g)) binmap.set(m[2], m[1]);
|
|
866
|
+
}
|
|
867
|
+
const images = /* @__PURE__ */ new Map();
|
|
868
|
+
const refs = /* @__PURE__ */ new Set();
|
|
869
|
+
for (const m of secXml.matchAll(/binaryItemIDRef="([^"]+)"/g)) refs.add(m[1]);
|
|
870
|
+
for (const ref of refs) {
|
|
871
|
+
let href = binmap.get(ref);
|
|
872
|
+
if (!href) {
|
|
873
|
+
const cand = zip.file(new RegExp(`BinData/.*${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i"))[0];
|
|
874
|
+
href = cand?.name;
|
|
875
|
+
}
|
|
876
|
+
if (!href) continue;
|
|
877
|
+
const f = zip.file(href) ?? zip.file("Contents/" + href);
|
|
878
|
+
if (!f) continue;
|
|
879
|
+
const bytes = await f.async("uint8array");
|
|
880
|
+
if (bytes.length > maxImg) {
|
|
881
|
+
warnings.push(`\uC774\uBBF8\uC9C0 ${href} ${(bytes.length / 1048576).toFixed(1)}MB \u2014 \uD55C\uB3C4 \uCD08\uACFC\uB85C \uC0DD\uB7B5`);
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
images.set(ref, { dataUri: `data:${sniffMime(href, bytes)};base64,${Buffer.from(bytes).toString("base64")}` });
|
|
885
|
+
}
|
|
886
|
+
const doc = createXmlParser().parseFromString(secXml, "text/xml");
|
|
887
|
+
const root = doc.documentElement;
|
|
888
|
+
if (!root) throw new KordocError("\uC139\uC158 XML \uD30C\uC2F1 \uC2E4\uD328");
|
|
889
|
+
const pagePr = findFirst(root, "pagePr");
|
|
890
|
+
const margin = pagePr ? findChildByLocalName(pagePr, "margin") : null;
|
|
891
|
+
const PW = num(pagePr, "width", 59528), PH = num(pagePr, "height", 84188);
|
|
892
|
+
const ML = num(margin, "left", 8504);
|
|
893
|
+
const MT = num(margin, "top", 5668) + num(margin, "header", 0);
|
|
894
|
+
const BODY_H = PH - MT - num(margin, "bottom", 4252) - num(margin, "footer", 0);
|
|
895
|
+
const BODY_W = PW - ML - num(margin, "right", 8504);
|
|
896
|
+
const ctx = {
|
|
897
|
+
svg: [],
|
|
898
|
+
geom: { PW, PH, ML, MT, BODY_W, BODY_H },
|
|
899
|
+
styles,
|
|
900
|
+
images,
|
|
901
|
+
warnings,
|
|
902
|
+
warned: /* @__PURE__ */ new Set(),
|
|
903
|
+
stats: { texts: 0, images: 0, tables: 0 }
|
|
904
|
+
};
|
|
905
|
+
for (const p of elements(root)) {
|
|
906
|
+
if (ln(p) !== "p") continue;
|
|
907
|
+
const segs = findChildByLocalName(p, "linesegarray");
|
|
908
|
+
const first = segs ? elements(segs)[0] : null;
|
|
909
|
+
if (first && num(first, "vertpos") > BODY_H) continue;
|
|
910
|
+
drawPara(p, ML, MT, BODY_W, ctx, 0);
|
|
911
|
+
}
|
|
912
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pt(PW)} ${pt(PH)}" width="${pt(PW)}pt" height="${pt(PH)}pt" font-family="'HCR Batang','\uD568\uCD08\uB86C\uBC14\uD0D5','Hancom Batang',AppleMyungjo,'Noto Serif CJK KR',serif" xml:space="preserve">
|
|
913
|
+
<rect width="100%" height="100%" fill="white"/>
|
|
914
|
+
${ctx.svg.join("\n")}
|
|
915
|
+
</svg>`;
|
|
916
|
+
return { svg, width: Math.round(PW) / 100, height: Math.round(PH) / 100, warnings, stats: ctx.stats };
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
export {
|
|
920
|
+
MAX_DECOMPRESS_SIZE,
|
|
921
|
+
MAX_ZIP_ENTRIES,
|
|
922
|
+
clampSpan,
|
|
923
|
+
MAX_XML_DEPTH,
|
|
924
|
+
createSectionShared,
|
|
925
|
+
createXmlParser,
|
|
926
|
+
applyPageText,
|
|
927
|
+
findChildByLocalName,
|
|
928
|
+
extractTextFromNode,
|
|
929
|
+
charWidthEm1000,
|
|
930
|
+
SPACE_EM_FIXED,
|
|
931
|
+
SPACE_EM_FONT,
|
|
932
|
+
measureTextWidth,
|
|
933
|
+
simulateWrap,
|
|
934
|
+
simulateWrapKeepWord,
|
|
935
|
+
fitRatioForFewerLines,
|
|
936
|
+
renderHwpxToSvg
|
|
937
|
+
};
|
|
938
|
+
//# sourceMappingURL=chunk-UR34PYAC.js.map
|