kordoc 3.13.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.
- package/README.md +34 -1
- package/dist/{-MRPDPE2X.js → -H7N62NZU.js} +24 -13
- package/dist/{chunk-33CIKNL4.js → chunk-5RHRQ7HK.js} +479 -258
- package/dist/chunk-5RHRQ7HK.js.map +1 -0
- package/dist/{chunk-MEXUFQVC.cjs → chunk-BIP4JAVG.cjs} +5 -5
- package/dist/chunk-BIP4JAVG.cjs.map +1 -0
- package/dist/chunk-DCZVOIEO.cjs.map +1 -1
- package/dist/chunk-FGL7BH4P.js +1019 -0
- package/dist/chunk-FGL7BH4P.js.map +1 -0
- package/dist/chunk-GS7T56RP.cjs.map +1 -1
- package/dist/{chunk-Q6UXVJXI.js → chunk-IC56KHBL.js} +2 -2
- package/dist/chunk-JFNWJFKP.js +185 -0
- package/dist/chunk-JFNWJFKP.js.map +1 -0
- package/dist/{chunk-YXOT7P5B.js → chunk-LYL4IL3E.js} +5 -5
- package/dist/{chunk-YXOT7P5B.js.map → chunk-LYL4IL3E.js.map} +1 -1
- package/dist/{chunk-O4WFPOFN.js → chunk-Q7EGGW3H.js} +5 -5
- package/dist/{chunk-O4WFPOFN.js.map → chunk-Q7EGGW3H.js.map} +1 -1
- package/dist/{chunk-TWACKS3K.js → chunk-WD5NUUU6.js} +571 -729
- package/dist/chunk-WD5NUUU6.js.map +1 -0
- package/dist/cli.js +125 -12
- package/dist/cli.js.map +1 -1
- package/dist/formula-5NKVS2LR.cjs.map +1 -1
- package/dist/index.cjs +1668 -449
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +144 -6
- package/dist/index.d.ts +144 -6
- package/dist/index.js +1502 -283
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +93 -12
- package/dist/mcp.js.map +1 -1
- package/dist/page-range-P7SDW6LR.cjs.map +1 -1
- package/dist/{parser-Z3IL2ESY.js → parser-5G247XYD.js} +54 -6
- package/dist/parser-5G247XYD.js.map +1 -0
- package/dist/{parser-DZBI3EA2.cjs → parser-6V7F3GET.cjs} +66 -18
- package/dist/parser-6V7F3GET.cjs.map +1 -0
- package/dist/{parser-XUNGOH76.js → parser-HBHCD7TE.js} +55 -7
- package/dist/parser-HBHCD7TE.js.map +1 -0
- package/dist/provider-G4C2V2PD.cjs.map +1 -1
- package/dist/render-VA4NHROW.js +10 -0
- package/dist/seal-UVDRGHEF.js +10 -0
- package/dist/seal-UVDRGHEF.js.map +1 -0
- package/dist/{watch-G5NFIT5L.js → watch-HIYNOSFD.js} +7 -5
- package/dist/{watch-G5NFIT5L.js.map → watch-HIYNOSFD.js.map} +1 -1
- package/package.json +4 -3
- package/dist/chunk-33CIKNL4.js.map +0 -1
- package/dist/chunk-MEXUFQVC.cjs.map +0 -1
- package/dist/chunk-TWACKS3K.js.map +0 -1
- package/dist/parser-DZBI3EA2.cjs.map +0 -1
- package/dist/parser-XUNGOH76.js.map +0 -1
- package/dist/parser-Z3IL2ESY.js.map +0 -1
- package/dist/render-VZ4OGHZU.js +0 -9
- /package/dist/{-MRPDPE2X.js.map → -H7N62NZU.js.map} +0 -0
- /package/dist/{chunk-Q6UXVJXI.js.map → chunk-IC56KHBL.js.map} +0 -0
- /package/dist/{render-VZ4OGHZU.js.map → render-VA4NHROW.js.map} +0 -0
|
@@ -1,62 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_CHAR,
|
|
4
|
+
DEFAULT_PARA_GEOM,
|
|
5
|
+
MAX_DECOMPRESS_SIZE,
|
|
6
|
+
createXmlParser,
|
|
7
|
+
findChildByLocalName,
|
|
8
|
+
parseRenderStyles
|
|
9
|
+
} from "./chunk-JFNWJFKP.js";
|
|
2
10
|
import {
|
|
3
11
|
KordocError
|
|
4
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-IC56KHBL.js";
|
|
5
13
|
|
|
6
14
|
// src/render/svg-render.ts
|
|
7
15
|
import JSZip from "jszip";
|
|
8
16
|
|
|
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
17
|
// src/render/layout.ts
|
|
61
18
|
function toInt32(v, fallback = 0) {
|
|
62
19
|
if (v == null || v === "") return fallback;
|
|
@@ -370,80 +327,162 @@ function fitRatioForFewerLines(text, firstWidth, contWidth, height, baseRatio, m
|
|
|
370
327
|
return null;
|
|
371
328
|
}
|
|
372
329
|
|
|
373
|
-
// src/render/
|
|
374
|
-
var
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if (
|
|
384
|
-
return
|
|
385
|
-
}
|
|
386
|
-
function
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
const
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
330
|
+
// src/render/reflow.ts
|
|
331
|
+
var LINESEG_FLAGS = "393216";
|
|
332
|
+
var BASELINE_RATIO = 0.85;
|
|
333
|
+
function ln(el) {
|
|
334
|
+
return (el.tagName || "").replace(/^[^:]+:/, "");
|
|
335
|
+
}
|
|
336
|
+
function elements(el) {
|
|
337
|
+
const out = [];
|
|
338
|
+
const kids = el.childNodes;
|
|
339
|
+
if (!kids) return out;
|
|
340
|
+
for (let i = 0; i < kids.length; i++) if (kids[i].nodeType === 1) out.push(kids[i]);
|
|
341
|
+
return out;
|
|
342
|
+
}
|
|
343
|
+
function num(el, attr, fallback = 0) {
|
|
344
|
+
return el ? toInt32(el.getAttribute(attr) ?? void 0, fallback) : fallback;
|
|
345
|
+
}
|
|
346
|
+
function shiftParaVert(p, delta) {
|
|
347
|
+
for (const lsa of elements(p)) {
|
|
348
|
+
if (ln(lsa) !== "linesegarray") continue;
|
|
349
|
+
for (const seg of elements(lsa)) {
|
|
350
|
+
if (ln(seg) !== "lineseg") continue;
|
|
351
|
+
seg.setAttribute("vertpos", String(num(seg, "vertpos") + delta));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function pitchFor(height, geom) {
|
|
356
|
+
const v = geom.lineSpacingValue;
|
|
357
|
+
switch (geom.lineSpacingType) {
|
|
358
|
+
case "PERCENT":
|
|
359
|
+
return Math.round(height * v / 100);
|
|
360
|
+
case "FIXED":
|
|
361
|
+
return v > 0 ? v : Math.round(height * 1.6);
|
|
362
|
+
// 고정 줄높이(HWPUNIT)
|
|
363
|
+
case "AT_LEAST":
|
|
364
|
+
return Math.max(v, height);
|
|
365
|
+
default:
|
|
366
|
+
return Math.round(height * 1.6);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function reflowPara(p, doc, styles, areaW, startV, mode) {
|
|
370
|
+
const m = buildPara(p);
|
|
371
|
+
if (m.segs.length > 0) return null;
|
|
372
|
+
const realIdx = [];
|
|
373
|
+
let text = "";
|
|
374
|
+
for (let i = 0; i < m.chars.length; i++) {
|
|
375
|
+
const ch = m.chars[i].ch;
|
|
376
|
+
if (ch === "") continue;
|
|
377
|
+
for (let u = 0; u < ch.length; u++) realIdx.push(i);
|
|
378
|
+
text += ch;
|
|
379
|
+
}
|
|
380
|
+
const geom = styles.paraGeom.get(m.paraPrId ?? "") ?? DEFAULT_PARA_GEOM;
|
|
381
|
+
let domChar = DEFAULT_CHAR;
|
|
382
|
+
for (const c of m.chars) {
|
|
383
|
+
if (c.ch !== "" && c.prId != null) {
|
|
384
|
+
const st = styles.charPr.get(c.prId);
|
|
385
|
+
if (st) {
|
|
386
|
+
domChar = st;
|
|
387
|
+
break;
|
|
415
388
|
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const height = domChar.height || 1e3;
|
|
392
|
+
const ratio = domChar.ratio || 100;
|
|
393
|
+
const spacingPct = domChar.spacing || 0;
|
|
394
|
+
const marginL = geom.marginLeft;
|
|
395
|
+
const avail = Math.max(1e3, areaW - marginL - geom.marginRight);
|
|
396
|
+
const firstWidth = avail;
|
|
397
|
+
const contWidth = Math.max(500, avail + Math.min(0, geom.marginIntent));
|
|
398
|
+
const contHorz = marginL - Math.min(0, geom.marginIntent);
|
|
399
|
+
const wrap = text.length === 0 ? { lines: 1, starts: [0], lastLineWidth: 0 } : simulateWrap(text, firstWidth, contWidth, height, ratio, mode, { spacingPct });
|
|
400
|
+
const pitch = pitchFor(height, geom);
|
|
401
|
+
const baseline = Math.round(height * BASELINE_RATIO);
|
|
402
|
+
const spacing = Math.max(0, pitch - height);
|
|
403
|
+
const lsa = doc.createElement("hp:linesegarray");
|
|
404
|
+
for (let li = 0; li < wrap.starts.length; li++) {
|
|
405
|
+
const startReal = wrap.starts[li];
|
|
406
|
+
const textpos = startReal < realIdx.length ? realIdx[startReal] : m.chars.length;
|
|
407
|
+
const vertpos = startV + li * pitch;
|
|
408
|
+
const isFirst = li === 0;
|
|
409
|
+
const seg = doc.createElement("hp:lineseg");
|
|
410
|
+
seg.setAttribute("textpos", String(textpos));
|
|
411
|
+
seg.setAttribute("vertpos", String(vertpos));
|
|
412
|
+
seg.setAttribute("vertsize", String(height));
|
|
413
|
+
seg.setAttribute("textheight", String(height));
|
|
414
|
+
seg.setAttribute("baseline", String(baseline));
|
|
415
|
+
seg.setAttribute("spacing", String(spacing));
|
|
416
|
+
seg.setAttribute("horzpos", String(isFirst ? marginL : contHorz));
|
|
417
|
+
seg.setAttribute("horzsize", String(isFirst ? firstWidth : contWidth));
|
|
418
|
+
seg.setAttribute("flags", LINESEG_FLAGS);
|
|
419
|
+
lsa.appendChild(seg);
|
|
420
|
+
}
|
|
421
|
+
p.appendChild(lsa);
|
|
422
|
+
const textBottom = startV + wrap.starts.length * pitch;
|
|
423
|
+
let objBottom = startV;
|
|
424
|
+
for (const o of m.objs) {
|
|
425
|
+
const h = o.tag === "tbl" ? Math.max(o.height, measureTableHeight(o.el)) : o.height;
|
|
426
|
+
objBottom = Math.max(objBottom, startV + h);
|
|
427
|
+
}
|
|
428
|
+
return { paraBottom: Math.max(textBottom, objBottom), spaceAfter: geom.spaceAfter };
|
|
429
|
+
}
|
|
430
|
+
function reflowTablesIn(p, doc, styles, mode, counter) {
|
|
431
|
+
for (const run of elements(p)) {
|
|
432
|
+
if (ln(run) !== "run") continue;
|
|
433
|
+
for (const obj of elements(run)) {
|
|
434
|
+
if (ln(obj) !== "tbl") continue;
|
|
435
|
+
for (const tr of elements(obj)) {
|
|
436
|
+
if (ln(tr) !== "tr") continue;
|
|
437
|
+
for (const tc of elements(tr)) {
|
|
438
|
+
if (ln(tc) !== "tc") continue;
|
|
439
|
+
const csz = findChildByLocalName(tc, "cellSz");
|
|
440
|
+
const cm = findChildByLocalName(tc, "cellMargin");
|
|
441
|
+
const cellW = num(csz, "width");
|
|
442
|
+
const mL = cm ? num(cm, "left", 141) : 141;
|
|
443
|
+
const mR = cm ? num(cm, "right", 141) : 141;
|
|
444
|
+
const areaW = Math.max(500, cellW - mL - mR);
|
|
445
|
+
const sub = findChildByLocalName(tc, "subList");
|
|
446
|
+
if (sub) reflowBlockFlow(sub, doc, styles, areaW, mode, counter, 0);
|
|
447
|
+
}
|
|
430
448
|
}
|
|
431
449
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function reflowBlockFlow(container, doc, styles, areaW, mode, counter, bodyH) {
|
|
453
|
+
let cursorV = 0;
|
|
454
|
+
let prevSpaceAfter = 0;
|
|
455
|
+
for (const p of elements(container)) {
|
|
456
|
+
if (ln(p) !== "p") continue;
|
|
457
|
+
reflowTablesIn(p, doc, styles, mode, counter);
|
|
458
|
+
const g = styles.paraGeom.get(p.getAttribute("paraPrIDRef") ?? "");
|
|
459
|
+
const startV = cursorV + prevSpaceAfter + (g?.spaceBefore ?? 0);
|
|
460
|
+
const res = reflowPara(p, doc, styles, areaW, startV, mode);
|
|
461
|
+
if (res) {
|
|
462
|
+
const paraH = res.paraBottom - startV;
|
|
463
|
+
if (bodyH > 0 && startV > 0 && res.paraBottom > bodyH && paraH <= bodyH) {
|
|
464
|
+
shiftParaVert(p, -startV);
|
|
465
|
+
cursorV = paraH;
|
|
466
|
+
} else {
|
|
467
|
+
cursorV = res.paraBottom;
|
|
468
|
+
}
|
|
469
|
+
prevSpaceAfter = res.spaceAfter;
|
|
470
|
+
counter.n++;
|
|
436
471
|
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function reflowSection(root, styles, geom, mode = "keep") {
|
|
475
|
+
const doc = root.ownerDocument;
|
|
476
|
+
const counter = { n: 0 };
|
|
477
|
+
reflowBlockFlow(root, doc, styles, geom.BODY_W, mode, counter, geom.BODY_H);
|
|
478
|
+
return counter.n;
|
|
440
479
|
}
|
|
441
480
|
|
|
442
481
|
// src/render/svg-render.ts
|
|
443
|
-
function
|
|
482
|
+
function ln2(el) {
|
|
444
483
|
return (el.tagName || "").replace(/^[^:]+:/, "");
|
|
445
484
|
}
|
|
446
|
-
function
|
|
485
|
+
function elements2(el) {
|
|
447
486
|
const out = [];
|
|
448
487
|
const children = el.childNodes;
|
|
449
488
|
if (!children) return out;
|
|
@@ -452,13 +491,13 @@ function elements(el) {
|
|
|
452
491
|
}
|
|
453
492
|
return out;
|
|
454
493
|
}
|
|
455
|
-
function
|
|
494
|
+
function num2(el, attr, fallback = 0) {
|
|
456
495
|
return el ? toInt32(el.getAttribute(attr) ?? void 0, fallback) : fallback;
|
|
457
496
|
}
|
|
458
497
|
function findFirst(el, name, depth = 0) {
|
|
459
498
|
if (depth > 64) return null;
|
|
460
|
-
for (const ch of
|
|
461
|
-
if (
|
|
499
|
+
for (const ch of elements2(el)) {
|
|
500
|
+
if (ln2(ch) === name) return ch;
|
|
462
501
|
const found = findFirst(ch, name, depth + 1);
|
|
463
502
|
if (found) return found;
|
|
464
503
|
}
|
|
@@ -469,59 +508,80 @@ function escapeXml(s) {
|
|
|
469
508
|
}
|
|
470
509
|
var OBJ_TAGS = /* @__PURE__ */ new Set(["tbl", "pic", "container", "equation", "rect", "ellipse", "polygon", "curv", "line", "arc", "ole", "textart"]);
|
|
471
510
|
var pt = (u) => String(Math.round(u) / 100);
|
|
511
|
+
function emit(ctx, s) {
|
|
512
|
+
ctx.pages[ctx.page].push(s);
|
|
513
|
+
}
|
|
472
514
|
function warnOnce(ctx, key, msg) {
|
|
473
515
|
if (ctx.warned.has(key)) return;
|
|
474
516
|
ctx.warned.add(key);
|
|
475
517
|
ctx.warnings.push(msg);
|
|
476
518
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
519
|
+
var CHAR_CTRL_1SLOT = /* @__PURE__ */ new Set(["lineBreak", "hyphen", "nbSpace", "fwSpace"]);
|
|
520
|
+
function pushFillers(chars, n, prId) {
|
|
521
|
+
for (let i = 0; i < n; i++) chars.push({ ch: "", prId });
|
|
522
|
+
}
|
|
523
|
+
function pushTextSlots(t, chars, prId, depth) {
|
|
524
|
+
if (depth > 32) return;
|
|
525
|
+
const kids = t.childNodes;
|
|
526
|
+
if (!kids) return;
|
|
527
|
+
for (let i = 0; i < kids.length; i++) {
|
|
528
|
+
const c = kids[i];
|
|
529
|
+
if (c.nodeType === 3) {
|
|
530
|
+
for (const cp of c.textContent ?? "") {
|
|
531
|
+
chars.push({ ch: cp, prId });
|
|
532
|
+
if (cp.length === 2) chars.push({ ch: "", prId });
|
|
533
|
+
}
|
|
534
|
+
} else if (c.nodeType === 1) {
|
|
535
|
+
const el = c;
|
|
536
|
+
const tag = ln2(el);
|
|
537
|
+
if (tag === "tab") {
|
|
538
|
+
pushFillers(chars, 8, prId);
|
|
539
|
+
} else if (CHAR_CTRL_1SLOT.has(tag)) {
|
|
540
|
+
chars.push({ ch: tag === "nbSpace" || tag === "fwSpace" ? " " : "", prId });
|
|
541
|
+
} else {
|
|
542
|
+
pushTextSlots(el, chars, prId, depth + 1);
|
|
543
|
+
}
|
|
487
544
|
}
|
|
488
|
-
}
|
|
489
|
-
walk(t, 0);
|
|
490
|
-
return s;
|
|
545
|
+
}
|
|
491
546
|
}
|
|
492
547
|
function buildPara(p) {
|
|
493
548
|
const chars = [];
|
|
494
549
|
const objs = [];
|
|
495
550
|
let segs = [];
|
|
496
|
-
for (const runEl of
|
|
497
|
-
const tag =
|
|
551
|
+
for (const runEl of elements2(p)) {
|
|
552
|
+
const tag = ln2(runEl);
|
|
498
553
|
if (tag === "run") {
|
|
499
554
|
const prId = runEl.getAttribute("charPrIDRef");
|
|
500
|
-
for (const ch of
|
|
501
|
-
const cn =
|
|
555
|
+
for (const ch of elements2(runEl)) {
|
|
556
|
+
const cn = ln2(ch);
|
|
502
557
|
if (cn === "t") {
|
|
503
|
-
|
|
558
|
+
pushTextSlots(ch, chars, prId, 0);
|
|
504
559
|
} else if (OBJ_TAGS.has(cn)) {
|
|
505
560
|
const sz = findChildByLocalName(ch, "sz");
|
|
506
561
|
const pos = findChildByLocalName(ch, "pos");
|
|
562
|
+
const w = num2(sz, "width") || num2(findChildByLocalName(ch, "curSz"), "width") || num2(findChildByLocalName(ch, "orgSz"), "width");
|
|
563
|
+
const h = num2(sz, "height") || num2(findChildByLocalName(ch, "curSz"), "height") || num2(findChildByLocalName(ch, "orgSz"), "height");
|
|
507
564
|
objs.push({
|
|
508
565
|
el: ch,
|
|
509
566
|
tag: cn,
|
|
510
567
|
index: chars.length,
|
|
511
568
|
inline: pos?.getAttribute("treatAsChar") === "1",
|
|
512
|
-
width:
|
|
513
|
-
height:
|
|
569
|
+
width: w,
|
|
570
|
+
height: h
|
|
514
571
|
});
|
|
572
|
+
pushFillers(chars, 8, prId);
|
|
573
|
+
} else {
|
|
574
|
+
pushFillers(chars, 8, prId);
|
|
515
575
|
}
|
|
516
576
|
}
|
|
517
577
|
} else if (tag === "linesegarray") {
|
|
518
|
-
segs =
|
|
519
|
-
textpos:
|
|
520
|
-
vertpos:
|
|
521
|
-
horzpos:
|
|
522
|
-
horzsize:
|
|
523
|
-
textheight:
|
|
524
|
-
baseline:
|
|
578
|
+
segs = elements2(runEl).filter((s) => ln2(s) === "lineseg").map((s) => ({
|
|
579
|
+
textpos: num2(s, "textpos"),
|
|
580
|
+
vertpos: num2(s, "vertpos"),
|
|
581
|
+
horzpos: num2(s, "horzpos"),
|
|
582
|
+
horzsize: num2(s, "horzsize"),
|
|
583
|
+
textheight: num2(s, "textheight", 1e3),
|
|
584
|
+
baseline: num2(s, "baseline", 850)
|
|
525
585
|
}));
|
|
526
586
|
}
|
|
527
587
|
}
|
|
@@ -569,38 +629,41 @@ function advanceTo(m, styles, plan, upto) {
|
|
|
569
629
|
for (const o of m.objs) if (o.inline && o.index >= plan.start && o.index < upto) x += o.width;
|
|
570
630
|
return x;
|
|
571
631
|
}
|
|
572
|
-
function drawPara(p, ox, oy, areaW, ctx, depth) {
|
|
632
|
+
function drawPara(p, ox, oy, areaW, ctx, depth, segPages) {
|
|
573
633
|
if (depth > 16) {
|
|
574
634
|
warnOnce(ctx, "depth", "\uC911\uCCA9 \uAE4A\uC774 16 \uCD08\uACFC \u2014 \uC774\uD558 \uC0DD\uB7B5");
|
|
575
635
|
return;
|
|
576
636
|
}
|
|
577
637
|
const m = buildPara(p);
|
|
578
638
|
if (m.segs.length === 0) {
|
|
639
|
+
if (m.chars.some((c) => c.ch !== "")) {
|
|
640
|
+
warnOnce(ctx, "no-lineseg", "\uC870\uD310 \uCE90\uC2DC \uC5C6\uB294 \uBB38\uB2E8 \uD14D\uC2A4\uD2B8 \uC0DD\uB7B5 \u2014 reflow \uC635\uC158\uC73C\uB85C \uD569\uC131 \uAC00\uB2A5");
|
|
641
|
+
}
|
|
579
642
|
for (const o of m.objs) drawObject(o, ox, oy, 0, areaW, ctx, depth);
|
|
580
643
|
return;
|
|
581
644
|
}
|
|
582
645
|
const plans = planLines(m, ctx.styles);
|
|
583
646
|
const baseV = m.segs[0].vertpos;
|
|
584
|
-
for (
|
|
647
|
+
for (let li = 0; li < plans.length; li++) {
|
|
648
|
+
const plan = plans[li];
|
|
649
|
+
if (segPages && segPages[li] !== void 0) ctx.page = segPages[li];
|
|
585
650
|
const { seg } = plan;
|
|
586
651
|
let i = plan.start;
|
|
587
652
|
let cursor = ox + seg.horzpos + plan.xoff;
|
|
588
653
|
const y = oy + seg.vertpos + seg.baseline;
|
|
589
654
|
while (i < plan.end && i < m.chars.length) {
|
|
655
|
+
if (m.chars[i].ch === "") {
|
|
656
|
+
for (const o of m.objs) if (o.inline && o.index === i) cursor += o.width;
|
|
657
|
+
i++;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
590
660
|
const prId = m.chars[i].prId;
|
|
591
661
|
let j = i;
|
|
592
662
|
let piece = "";
|
|
593
|
-
while (j < plan.end && j < m.chars.length && m.chars[j].prId === prId) {
|
|
663
|
+
while (j < plan.end && j < m.chars.length && m.chars[j].prId === prId && m.chars[j].ch !== "") {
|
|
594
664
|
piece += m.chars[j].ch;
|
|
595
665
|
j++;
|
|
596
666
|
}
|
|
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
667
|
{
|
|
605
668
|
const cut = piece.search(/ {2,}/);
|
|
606
669
|
if (cut > 0) {
|
|
@@ -613,32 +676,71 @@ function drawPara(p, ox, oy, areaW, ctx, depth) {
|
|
|
613
676
|
}
|
|
614
677
|
}
|
|
615
678
|
const st = (prId != null ? ctx.styles.charPr.get(prId) : void 0) ?? DEFAULT_CHAR;
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
679
|
+
const renderSeg = (text, cx, hit) => {
|
|
680
|
+
const sw = measureTextWidth(text, st.height, st.ratio, { spacingPct: st.spacing }) * plan.scale;
|
|
681
|
+
if (hit) {
|
|
682
|
+
emit(ctx, `<rect x="${pt(cx)}" y="${pt(oy + seg.vertpos)}" width="${pt(sw)}" height="${pt(seg.textheight)}" fill="#ffd54f" fill-opacity="0.45"/>`);
|
|
683
|
+
}
|
|
684
|
+
if (text.trim().length > 0) {
|
|
685
|
+
const attrs = [`x="${pt(cx)}"`, `y="${pt(y)}"`, `font-size="${pt(st.height)}"`];
|
|
686
|
+
if ([...text].length > 1 && sw > 50) {
|
|
687
|
+
attrs.push(`textLength="${pt(sw)}"`, `lengthAdjust="${plan.scale < 1 ? "spacingAndGlyphs" : "spacing"}"`);
|
|
688
|
+
}
|
|
689
|
+
if (st.bold) attrs.push(`font-weight="bold"`);
|
|
690
|
+
if (st.italic) attrs.push(`font-style="italic"`);
|
|
691
|
+
if (st.underline) attrs.push(`text-decoration="underline"`);
|
|
692
|
+
if (st.color) attrs.push(`fill="${escapeXml(st.color)}"`);
|
|
693
|
+
emit(ctx, `<text ${attrs.join(" ")}>${escapeXml(text)}</text>`);
|
|
694
|
+
ctx.stats.texts++;
|
|
695
|
+
}
|
|
696
|
+
return sw;
|
|
697
|
+
};
|
|
698
|
+
const merged = [];
|
|
699
|
+
if (ctx.highlights.length > 0 && piece.trim().length > 0) {
|
|
700
|
+
const found = [];
|
|
701
|
+
const lower = piece.toLowerCase();
|
|
702
|
+
for (const term of ctx.highlights) {
|
|
703
|
+
for (let f = lower.indexOf(term); f !== -1; f = lower.indexOf(term, f + term.length)) {
|
|
704
|
+
found.push([f, f + term.length]);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
found.sort((a, b) => a[0] - b[0]);
|
|
708
|
+
for (const [s, e] of found) {
|
|
709
|
+
const tail = merged[merged.length - 1];
|
|
710
|
+
if (tail && s <= tail[1]) tail[1] = Math.max(tail[1], e);
|
|
711
|
+
else merged.push([s, e]);
|
|
621
712
|
}
|
|
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
713
|
}
|
|
629
|
-
|
|
630
|
-
|
|
714
|
+
if (merged.length === 0) {
|
|
715
|
+
cursor += renderSeg(piece, cursor, false);
|
|
716
|
+
} else {
|
|
717
|
+
let segCur = cursor;
|
|
718
|
+
let last = 0;
|
|
719
|
+
for (const [s, e] of merged) {
|
|
720
|
+
segCur += renderSeg(piece.slice(last, s), segCur, false);
|
|
721
|
+
segCur += renderSeg(piece.slice(s, e), segCur, true);
|
|
722
|
+
last = e;
|
|
723
|
+
}
|
|
724
|
+
segCur += renderSeg(piece.slice(last), segCur, false);
|
|
725
|
+
cursor = segCur;
|
|
726
|
+
}
|
|
631
727
|
i = j;
|
|
632
728
|
}
|
|
633
729
|
}
|
|
634
730
|
for (const o of m.objs) {
|
|
635
731
|
if (o.inline) {
|
|
636
|
-
let
|
|
637
|
-
for (
|
|
732
|
+
let planIdx = 0;
|
|
733
|
+
for (let k = 0; k < plans.length; k++) {
|
|
734
|
+
const pl = plans[k];
|
|
735
|
+
if (pl.start <= o.index && (o.index < pl.end || k === plans.length - 1)) planIdx = k;
|
|
736
|
+
}
|
|
737
|
+
const plan = plans[planIdx];
|
|
738
|
+
if (segPages && segPages[planIdx] !== void 0) ctx.page = segPages[planIdx];
|
|
638
739
|
const x = ox + plan.seg.horzpos + plan.xoff + advanceTo(m, ctx.styles, plan, o.index);
|
|
639
740
|
const yTop = oy + plan.seg.vertpos + Math.max(0, plan.seg.baseline - o.height);
|
|
640
741
|
drawObject(o, x, yTop, baseV, areaW, ctx, depth);
|
|
641
742
|
} else {
|
|
743
|
+
if (segPages && segPages[0] !== void 0) ctx.page = segPages[0];
|
|
642
744
|
const { x, y } = anchorObject(o, ox, oy, baseV, areaW, ctx);
|
|
643
745
|
drawObject(o, x, y, baseV, areaW, ctx, depth);
|
|
644
746
|
}
|
|
@@ -648,11 +750,11 @@ function anchorObject(o, ox, oy, baseV, areaW, ctx) {
|
|
|
648
750
|
const { PW, PH, ML, MT, BODY_W, BODY_H } = ctx.geom;
|
|
649
751
|
const pos = findChildByLocalName(o.el, "pos");
|
|
650
752
|
const om = findChildByLocalName(o.el, "outMargin");
|
|
651
|
-
const omT =
|
|
753
|
+
const omT = num2(om, "top"), omB = num2(om, "bottom");
|
|
652
754
|
const w = o.width, h = o.height;
|
|
653
755
|
if (!pos) return { x: ox, y: oy + baseV };
|
|
654
|
-
const vo =
|
|
655
|
-
const ho =
|
|
756
|
+
const vo = num2(pos, "vertOffset");
|
|
757
|
+
const ho = num2(pos, "horzOffset");
|
|
656
758
|
const vrel = pos.getAttribute("vertRelTo") ?? "PARA";
|
|
657
759
|
const hrel = pos.getAttribute("horzRelTo") ?? "PARA";
|
|
658
760
|
const va = pos.getAttribute("vertAlign") ?? "TOP";
|
|
@@ -684,25 +786,74 @@ function drawObject(o, x, y, baseV, areaW, ctx, depth) {
|
|
|
684
786
|
if (o.tag === "tbl") drawTable(o.el, x, y, ctx, depth + 1);
|
|
685
787
|
else if (o.tag === "pic") drawPic(o.el, x, y, ctx);
|
|
686
788
|
else if (o.tag === "container") {
|
|
687
|
-
for (const ch of
|
|
688
|
-
const tag =
|
|
789
|
+
for (const ch of elements2(o.el)) {
|
|
790
|
+
const tag = ln2(ch);
|
|
689
791
|
if (!OBJ_TAGS.has(tag)) continue;
|
|
690
792
|
const sz = findChildByLocalName(ch, "sz");
|
|
691
793
|
const off = findChildByLocalName(ch, "offset");
|
|
692
|
-
const sub = { el: ch, tag, index: 0, inline: true, width:
|
|
693
|
-
drawObject(sub, x +
|
|
794
|
+
const sub = { el: ch, tag, index: 0, inline: true, width: num2(sz, "width"), height: num2(sz, "height") };
|
|
795
|
+
drawObject(sub, x + num2(off, "x"), y + num2(off, "y"), baseV, areaW, ctx, depth + 1);
|
|
694
796
|
}
|
|
695
797
|
} else if (o.tag === "equation") {
|
|
696
798
|
warnOnce(ctx, "equation", "\uC218\uC2DD \uAC1C\uCCB4\uB294 \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5");
|
|
799
|
+
} else if (SHAPE_TAGS.has(o.tag)) {
|
|
800
|
+
drawShape(o, x, y, ctx, depth);
|
|
697
801
|
} else {
|
|
698
|
-
warnOnce(ctx, `shape:${o.tag}`, `\
|
|
802
|
+
warnOnce(ctx, `shape:${o.tag}`, `\uAC1C\uCCB4(${o.tag}) \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5`);
|
|
699
803
|
}
|
|
700
804
|
}
|
|
701
|
-
|
|
805
|
+
var SHAPE_TAGS = /* @__PURE__ */ new Set(["rect", "ellipse", "line", "polygon", "curv", "arc"]);
|
|
806
|
+
function shapeStrokePt(v) {
|
|
807
|
+
return Math.max(0.2, v / 100 * 2.834645);
|
|
808
|
+
}
|
|
809
|
+
function drawShape(o, x, y, ctx, depth) {
|
|
810
|
+
const el = o.el;
|
|
811
|
+
const orgSz = findChildByLocalName(el, "orgSz");
|
|
812
|
+
const curSz = findChildByLocalName(el, "curSz");
|
|
813
|
+
const ow = num2(orgSz, "width"), oh = num2(orgSz, "height");
|
|
814
|
+
const w = num2(curSz, "width") || ow || o.width;
|
|
815
|
+
const h = num2(curSz, "height") || oh || o.height;
|
|
816
|
+
const sx = ow > 0 ? w / ow : 1;
|
|
817
|
+
const sy = oh > 0 ? h / oh : 1;
|
|
818
|
+
const lineShape = findChildByLocalName(el, "lineShape");
|
|
819
|
+
const lstyle = lineShape?.getAttribute("style") ?? "SOLID";
|
|
820
|
+
const strokeCol = lineShape?.getAttribute("color") || "#000000";
|
|
821
|
+
const hasStroke = lstyle !== "NONE";
|
|
822
|
+
const strokeW = hasStroke ? shapeStrokePt(lineShape ? num2(lineShape, "width") : 33) : 0;
|
|
823
|
+
const dash = /DASH|DOT/.test(lstyle) ? ` stroke-dasharray="${lstyle.includes("DOT") ? "1,1.5" : "3,1.5"}"` : "";
|
|
824
|
+
const strokeAttr = hasStroke ? ` stroke="${escapeXml(strokeCol)}" stroke-width="${strokeW.toFixed(2)}"${dash}` : "";
|
|
825
|
+
const fillBrush = findChildByLocalName(el, "fillBrush");
|
|
826
|
+
const winBrush = fillBrush ? findChildByLocalName(fillBrush, "winBrush") : null;
|
|
827
|
+
const face = winBrush?.getAttribute("faceColor");
|
|
828
|
+
const fill = face && face.toLowerCase() !== "none" ? face : "none";
|
|
829
|
+
const fillAttr = ` fill="${fill === "none" ? "none" : escapeXml(fill)}"`;
|
|
830
|
+
if (o.tag === "rect") {
|
|
831
|
+
emit(ctx, `<rect x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}"${fillAttr}${strokeAttr}/>`);
|
|
832
|
+
} else if (o.tag === "ellipse") {
|
|
833
|
+
emit(ctx, `<ellipse cx="${pt(x + w / 2)}" cy="${pt(y + h / 2)}" rx="${pt(w / 2)}" ry="${pt(h / 2)}"${fillAttr}${strokeAttr}/>`);
|
|
834
|
+
} else if (o.tag === "line") {
|
|
835
|
+
const s = findChildByLocalName(el, "startPt"), e = findChildByLocalName(el, "endPt");
|
|
836
|
+
const x1 = x + num2(s, "x") * sx, y1 = y + num2(s, "y") * sy;
|
|
837
|
+
const x2 = x + num2(e, "x") * sx, y2 = y + num2(e, "y") * sy;
|
|
838
|
+
emit(ctx, `<line x1="${pt(x1)}" y1="${pt(y1)}" x2="${pt(x2)}" y2="${pt(y2)}" stroke="${escapeXml(strokeCol)}" stroke-width="${(strokeW || 0.3).toFixed(2)}"${dash}/>`);
|
|
839
|
+
} else if (o.tag === "polygon" || o.tag === "curv") {
|
|
840
|
+
const pts = [];
|
|
841
|
+
for (const c of elements2(el)) if (ln2(c) === "pt") pts.push(`${pt(x + num2(c, "x") * sx)},${pt(y + num2(c, "y") * sy)}`);
|
|
842
|
+
if (pts.length >= 2) emit(ctx, `<polygon points="${pts.join(" ")}"${fillAttr}${strokeAttr}/>`);
|
|
843
|
+
} else if (o.tag === "arc") {
|
|
844
|
+
emit(ctx, `<ellipse cx="${pt(x + w / 2)}" cy="${pt(y + h / 2)}" rx="${pt(w / 2)}" ry="${pt(h / 2)}" fill="none"${strokeAttr || ` stroke="${escapeXml(strokeCol)}" stroke-width="0.3"`}/>`);
|
|
845
|
+
}
|
|
846
|
+
const dt = findChildByLocalName(el, "drawText");
|
|
847
|
+
const sub = dt ? findChildByLocalName(dt, "subList") : null;
|
|
848
|
+
if (sub) {
|
|
849
|
+
for (const p of elements2(sub)) if (ln2(p) === "p") drawPara(p, x, y, w, ctx, depth + 1);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function cellContentExtent(cell) {
|
|
702
853
|
if (!cell.sub) return 0;
|
|
703
854
|
let ext = 0;
|
|
704
|
-
for (const p of
|
|
705
|
-
if (
|
|
855
|
+
for (const p of elements2(cell.sub)) {
|
|
856
|
+
if (ln2(p) !== "p") continue;
|
|
706
857
|
const m = buildPara(p);
|
|
707
858
|
for (const s of m.segs) ext = Math.max(ext, s.vertpos + s.textheight);
|
|
708
859
|
const baseV = m.segs[0]?.vertpos ?? 0;
|
|
@@ -711,9 +862,9 @@ function cellContentExtent(cell, ctx) {
|
|
|
711
862
|
const pos = findChildByLocalName(o.el, "pos");
|
|
712
863
|
if ((pos?.getAttribute("vertRelTo") ?? "PARA") !== "PARA") continue;
|
|
713
864
|
const om = findChildByLocalName(o.el, "outMargin");
|
|
714
|
-
const pushed = baseV - (
|
|
865
|
+
const pushed = baseV - (num2(om, "top") + o.height + num2(om, "bottom"));
|
|
715
866
|
const anchor = pushed >= -100 ? pushed : baseV;
|
|
716
|
-
ext = Math.max(ext, anchor +
|
|
867
|
+
ext = Math.max(ext, anchor + num2(om, "top") + num2(pos, "vertOffset") + o.height);
|
|
717
868
|
}
|
|
718
869
|
}
|
|
719
870
|
return ext;
|
|
@@ -722,21 +873,15 @@ function edgeLine(x1, y1, x2, y2, e) {
|
|
|
722
873
|
const dash = /DASH|DOT/.test(e.type) ? ` stroke-dasharray="${e.type.includes("DOT") ? "1,1.5" : "3,1.5"}"` : "";
|
|
723
874
|
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
875
|
}
|
|
725
|
-
function
|
|
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");
|
|
876
|
+
function collectCells(tbl) {
|
|
732
877
|
const inMargin = findChildByLocalName(tbl, "inMargin");
|
|
733
|
-
const defL =
|
|
734
|
-
const defT =
|
|
878
|
+
const defL = num2(inMargin, "left", 141), defR = num2(inMargin, "right", 141);
|
|
879
|
+
const defT = num2(inMargin, "top", 141), defB = num2(inMargin, "bottom", 141);
|
|
735
880
|
const cells = [];
|
|
736
|
-
for (const tr of
|
|
737
|
-
if (
|
|
738
|
-
for (const tc of
|
|
739
|
-
if (
|
|
881
|
+
for (const tr of elements2(tbl)) {
|
|
882
|
+
if (ln2(tr) !== "tr") continue;
|
|
883
|
+
for (const tc of elements2(tr)) {
|
|
884
|
+
if (ln2(tc) !== "tc") continue;
|
|
740
885
|
const addr = findChildByLocalName(tc, "cellAddr");
|
|
741
886
|
const span = findChildByLocalName(tc, "cellSpan");
|
|
742
887
|
const csz = findChildByLocalName(tc, "cellSz");
|
|
@@ -744,28 +889,50 @@ function drawTable(tbl, tx, ty, ctx, depth) {
|
|
|
744
889
|
if (!addr || !csz) continue;
|
|
745
890
|
cells.push({
|
|
746
891
|
el: tc,
|
|
747
|
-
ca:
|
|
748
|
-
ra:
|
|
749
|
-
cs: Math.max(1,
|
|
750
|
-
rs: Math.max(1,
|
|
751
|
-
w:
|
|
752
|
-
h:
|
|
892
|
+
ca: num2(addr, "colAddr"),
|
|
893
|
+
ra: num2(addr, "rowAddr"),
|
|
894
|
+
cs: Math.max(1, num2(span, "colSpan", 1)),
|
|
895
|
+
rs: Math.max(1, num2(span, "rowSpan", 1)),
|
|
896
|
+
w: num2(csz, "width"),
|
|
897
|
+
h: num2(csz, "height"),
|
|
753
898
|
bfId: tc.getAttribute("borderFillIDRef"),
|
|
754
899
|
sub: findChildByLocalName(tc, "subList"),
|
|
755
|
-
marginL: cm ?
|
|
756
|
-
marginR: cm ?
|
|
757
|
-
marginT: cm ?
|
|
758
|
-
marginB: cm ?
|
|
900
|
+
marginL: cm ? num2(cm, "left", defL) : defL,
|
|
901
|
+
marginR: cm ? num2(cm, "right", defR) : defR,
|
|
902
|
+
marginT: cm ? num2(cm, "top", defT) : defT,
|
|
903
|
+
marginB: cm ? num2(cm, "bottom", defB) : defB
|
|
759
904
|
});
|
|
760
905
|
}
|
|
761
906
|
}
|
|
907
|
+
return cells;
|
|
908
|
+
}
|
|
909
|
+
function measureTableHeight(tbl) {
|
|
910
|
+
const cells = collectCells(tbl);
|
|
911
|
+
if (cells.length === 0 || cells.length > 4096) return 0;
|
|
912
|
+
const nRows = Math.max(...cells.map((c) => c.ra + c.rs));
|
|
913
|
+
const rowH = solveRowHeights(
|
|
914
|
+
cells.map((c) => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c) : void 0 })),
|
|
915
|
+
nRows
|
|
916
|
+
);
|
|
917
|
+
let sum = 0;
|
|
918
|
+
for (const h of rowH) sum += h;
|
|
919
|
+
return sum;
|
|
920
|
+
}
|
|
921
|
+
function drawTable(tbl, tx, ty, ctx, depth) {
|
|
922
|
+
if (depth > 16) {
|
|
923
|
+
warnOnce(ctx, "depth", "\uC911\uCCA9 \uAE4A\uC774 16 \uCD08\uACFC \u2014 \uC774\uD558 \uC0DD\uB7B5");
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
ctx.stats.tables++;
|
|
927
|
+
const tblSz = findChildByLocalName(tbl, "sz");
|
|
928
|
+
const cells = collectCells(tbl);
|
|
762
929
|
if (cells.length === 0 || cells.length > 4096) return;
|
|
763
930
|
const nCols = Math.max(...cells.map((c) => c.ca + c.cs));
|
|
764
931
|
const nRows = Math.max(...cells.map((c) => c.ra + c.rs));
|
|
765
932
|
const colCons = cells.map((c) => ({ a: c.ca, b: c.ca + c.cs, size: c.w }));
|
|
766
|
-
const colX = solveBoundaries(colCons, nCols,
|
|
933
|
+
const colX = solveBoundaries(colCons, nCols, num2(tblSz, "width") || void 0);
|
|
767
934
|
const rowH = solveRowHeights(
|
|
768
|
-
cells.map((c) => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c
|
|
935
|
+
cells.map((c) => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c) : void 0 })),
|
|
769
936
|
nRows
|
|
770
937
|
);
|
|
771
938
|
const rowY = [0];
|
|
@@ -779,55 +946,69 @@ function drawTable(tbl, tx, ty, ctx, depth) {
|
|
|
779
946
|
}));
|
|
780
947
|
for (const g of geom) {
|
|
781
948
|
const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : void 0;
|
|
782
|
-
if (bf?.fill) ctx
|
|
949
|
+
if (bf?.fill) emit(ctx, `<rect x="${pt(g.x)}" y="${pt(g.y)}" width="${pt(g.w)}" height="${pt(g.h)}" fill="${escapeXml(bf.fill)}"/>`);
|
|
783
950
|
}
|
|
784
951
|
for (const g of geom) {
|
|
785
952
|
const { c } = g;
|
|
786
953
|
if (!c.sub) continue;
|
|
787
954
|
const innerH = g.h - c.marginT - c.marginB;
|
|
788
|
-
const extent = cellContentExtent(c
|
|
955
|
+
const extent = cellContentExtent(c);
|
|
789
956
|
const va = c.sub.getAttribute("vertAlign") ?? "TOP";
|
|
790
957
|
let yoff = 0;
|
|
791
958
|
if (va === "CENTER") yoff = Math.max(0, (innerH - extent) / 2);
|
|
792
959
|
else if (va === "BOTTOM") yoff = Math.max(0, innerH - extent);
|
|
793
|
-
for (const p of
|
|
794
|
-
if (
|
|
960
|
+
for (const p of elements2(c.sub)) {
|
|
961
|
+
if (ln2(p) !== "p") continue;
|
|
795
962
|
drawPara(p, g.x + c.marginL, g.y + c.marginT + yoff, g.w - c.marginL - c.marginR, ctx, depth + 1);
|
|
796
963
|
}
|
|
797
964
|
}
|
|
798
965
|
for (const g of geom) {
|
|
799
966
|
const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : void 0;
|
|
800
967
|
if (!bf) continue;
|
|
801
|
-
if (bf.top) ctx
|
|
802
|
-
if (bf.bottom) ctx
|
|
803
|
-
if (bf.left) ctx
|
|
804
|
-
if (bf.right) ctx
|
|
968
|
+
if (bf.top) emit(ctx, edgeLine(g.x, g.y, g.x + g.w, g.y, bf.top));
|
|
969
|
+
if (bf.bottom) emit(ctx, edgeLine(g.x, g.y + g.h, g.x + g.w, g.y + g.h, bf.bottom));
|
|
970
|
+
if (bf.left) emit(ctx, edgeLine(g.x, g.y, g.x, g.y + g.h, bf.left));
|
|
971
|
+
if (bf.right) emit(ctx, edgeLine(g.x + g.w, g.y, g.x + g.w, g.y + g.h, bf.right));
|
|
805
972
|
}
|
|
806
973
|
}
|
|
974
|
+
function imageSymbol(loaded, ctx) {
|
|
975
|
+
if (!loaded.symId) {
|
|
976
|
+
loaded.symId = `bin${ctx.defs.length}`;
|
|
977
|
+
ctx.defs.push(
|
|
978
|
+
`<symbol id="${loaded.symId}" viewBox="0 0 100 100" preserveAspectRatio="none"><image width="100" height="100" preserveAspectRatio="none" href="${loaded.dataUri}"/></symbol>`
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
return loaded.symId;
|
|
982
|
+
}
|
|
807
983
|
function drawPic(pic, x, y, ctx) {
|
|
808
984
|
const sz = findChildByLocalName(pic, "sz");
|
|
809
|
-
const w =
|
|
985
|
+
const w = num2(sz, "width", 5669), h = num2(sz, "height", 5669);
|
|
810
986
|
const img = findFirst(pic, "img");
|
|
811
987
|
const ref = img?.getAttribute("binaryItemIDRef");
|
|
812
988
|
const loaded = ref != null ? ctx.images.get(ref) : void 0;
|
|
813
989
|
if (!loaded) {
|
|
814
|
-
ctx
|
|
990
|
+
emit(ctx, `<rect x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" fill="#eee" stroke="#c00" stroke-width="0.5"/>`);
|
|
815
991
|
warnOnce(ctx, `img:${ref}`, `\uC774\uBBF8\uC9C0 \uBC14\uC774\uB108\uB9AC \uB204\uB77D: ${ref ?? "(ref \uC5C6\uC74C)"}`);
|
|
816
992
|
return;
|
|
817
993
|
}
|
|
818
994
|
ctx.stats.images++;
|
|
819
|
-
const orgSz = findChildByLocalName(pic, "orgSz");
|
|
820
995
|
const clip = findChildByLocalName(pic, "imgClip");
|
|
821
|
-
const
|
|
822
|
-
const
|
|
823
|
-
const
|
|
824
|
-
const
|
|
996
|
+
const imgDim = findChildByLocalName(pic, "imgDim");
|
|
997
|
+
const orgSz = findChildByLocalName(pic, "orgSz");
|
|
998
|
+
const dimW = num2(imgDim, "dimwidth"), dimH = num2(imgDim, "dimheight");
|
|
999
|
+
const refW = dimW > 0 ? dimW : num2(orgSz, "width");
|
|
1000
|
+
const refH = dimH > 0 ? dimH : num2(orgSz, "height");
|
|
1001
|
+
const cl = num2(clip, "left"), ct = num2(clip, "top");
|
|
1002
|
+
const cr = num2(clip, "right", refW), cb = num2(clip, "bottom", refH);
|
|
1003
|
+
const cropped = refW > 0 && refH > 0 && clip != null && (cl > 0 || ct > 0 || cr < refW || cb < refH) && cr > cl && cb > ct;
|
|
1004
|
+
const symId = imageSymbol(loaded, ctx);
|
|
825
1005
|
if (cropped) {
|
|
826
|
-
|
|
827
|
-
|
|
1006
|
+
emit(
|
|
1007
|
+
ctx,
|
|
1008
|
+
`<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"><use href="#${symId}" x="0" y="0" width="${pt(refW)}" height="${pt(refH)}"/></svg>`
|
|
828
1009
|
);
|
|
829
1010
|
} else {
|
|
830
|
-
ctx
|
|
1011
|
+
emit(ctx, `<use href="#${symId}" x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}"/>`);
|
|
831
1012
|
}
|
|
832
1013
|
}
|
|
833
1014
|
function sniffMime(name, bytes) {
|
|
@@ -850,12 +1031,13 @@ async function renderHwpxToSvg(input, options) {
|
|
|
850
1031
|
if (!secFile) throw new KordocError("Contents/section0.xml \uC5C6\uC74C \u2014 HWPX\uAC00 \uC544\uB2C8\uAC70\uB098 \uC190\uC0C1\uB428");
|
|
851
1032
|
const secXml = await secFile.async("string");
|
|
852
1033
|
if (secXml.length > MAX_DECOMPRESS_SIZE) throw new KordocError("\uC139\uC158 XML\uC774 \uD5C8\uC6A9 \uD06C\uAE30\uB97C \uCD08\uACFC");
|
|
853
|
-
|
|
854
|
-
|
|
1034
|
+
const hasCache = /<(?:[A-Za-z][\w.-]*:)?linesegarray[\s/>]/.test(secXml);
|
|
1035
|
+
if (!hasCache && !options?.reflow) {
|
|
1036
|
+
throw new KordocError("\uC870\uD310 \uCE90\uC2DC(linesegarray) \uC5C6\uC74C \u2014 \uD55C\uCEF4\uC5D0\uC11C \uC800\uC7A5\uD55C HWPX\uB9CC \uB80C\uB354 \uAC00\uB2A5 (reflow \uC635\uC158\uC73C\uB85C \uD569\uC131 \uB80C\uB354 \uAC00\uB2A5)");
|
|
855
1037
|
}
|
|
856
1038
|
const warnings = [];
|
|
857
1039
|
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() };
|
|
1040
|
+
const styles = headFile ? parseRenderStyles(await headFile.async("string")) : { charPr: /* @__PURE__ */ new Map(), paraAlign: /* @__PURE__ */ new Map(), paraGeom: /* @__PURE__ */ new Map(), borderFill: /* @__PURE__ */ new Map() };
|
|
859
1041
|
if (!headFile) warnings.push("header.xml \uC5C6\uC74C \u2014 \uAE30\uBCF8 \uC2A4\uD0C0\uC77C\uB85C \uB80C\uB354");
|
|
860
1042
|
const binmap = /* @__PURE__ */ new Map();
|
|
861
1043
|
const hpf = zip.file(/content\.hpf$/i)[0];
|
|
@@ -864,10 +1046,17 @@ async function renderHwpxToSvg(input, options) {
|
|
|
864
1046
|
for (const m of man.matchAll(/<[^>]*\bid="([^"]+)"[^>]*\bhref="(BinData\/[^"]+)"[^>]*>/g)) binmap.set(m[1], m[2]);
|
|
865
1047
|
for (const m of man.matchAll(/<[^>]*\bhref="(BinData\/[^"]+)"[^>]*\bid="([^"]+)"[^>]*>/g)) binmap.set(m[2], m[1]);
|
|
866
1048
|
}
|
|
1049
|
+
const MAX_IMAGE_REFS = 256;
|
|
1050
|
+
const MAX_TOTAL_IMAGE_BYTES = 128 * 1024 * 1024;
|
|
867
1051
|
const images = /* @__PURE__ */ new Map();
|
|
868
1052
|
const refs = /* @__PURE__ */ new Set();
|
|
869
1053
|
for (const m of secXml.matchAll(/binaryItemIDRef="([^"]+)"/g)) refs.add(m[1]);
|
|
1054
|
+
let totalImgBytes = 0;
|
|
870
1055
|
for (const ref of refs) {
|
|
1056
|
+
if (images.size >= MAX_IMAGE_REFS) {
|
|
1057
|
+
warnings.push(`\uC774\uBBF8\uC9C0 ${refs.size}\uC885 \uC911 ${MAX_IMAGE_REFS}\uC885\uB9CC \uB85C\uB529 \u2014 \uAC1C\uC218 \uD55C\uB3C4 \uCD08\uACFC\uBD84 \uC0DD\uB7B5`);
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
871
1060
|
let href = binmap.get(ref);
|
|
872
1061
|
if (!href) {
|
|
873
1062
|
const cand = zip.file(new RegExp(`BinData/.*${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i"))[0];
|
|
@@ -881,6 +1070,11 @@ async function renderHwpxToSvg(input, options) {
|
|
|
881
1070
|
warnings.push(`\uC774\uBBF8\uC9C0 ${href} ${(bytes.length / 1048576).toFixed(1)}MB \u2014 \uD55C\uB3C4 \uCD08\uACFC\uB85C \uC0DD\uB7B5`);
|
|
882
1071
|
continue;
|
|
883
1072
|
}
|
|
1073
|
+
if (totalImgBytes + bytes.length > MAX_TOTAL_IMAGE_BYTES) {
|
|
1074
|
+
warnings.push(`\uC774\uBBF8\uC9C0 \uB204\uC801 ${Math.round(MAX_TOTAL_IMAGE_BYTES / 1048576)}MB \uD55C\uB3C4 \uCD08\uACFC \u2014 \uC774\uD6C4 \uC0DD\uB7B5`);
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
totalImgBytes += bytes.length;
|
|
884
1078
|
images.set(ref, { dataUri: `data:${sniffMime(href, bytes)};base64,${Buffer.from(bytes).toString("base64")}` });
|
|
885
1079
|
}
|
|
886
1080
|
const doc = createXmlParser().parseFromString(secXml, "text/xml");
|
|
@@ -888,44 +1082,71 @@ async function renderHwpxToSvg(input, options) {
|
|
|
888
1082
|
if (!root) throw new KordocError("\uC139\uC158 XML \uD30C\uC2F1 \uC2E4\uD328");
|
|
889
1083
|
const pagePr = findFirst(root, "pagePr");
|
|
890
1084
|
const margin = pagePr ? findChildByLocalName(pagePr, "margin") : null;
|
|
891
|
-
const PW =
|
|
892
|
-
const ML =
|
|
893
|
-
const MT =
|
|
894
|
-
const BODY_H = PH - MT -
|
|
895
|
-
const BODY_W = PW - ML -
|
|
1085
|
+
const PW = num2(pagePr, "width", 59528), PH = num2(pagePr, "height", 84188);
|
|
1086
|
+
const ML = num2(margin, "left", 8504);
|
|
1087
|
+
const MT = num2(margin, "top", 5668) + num2(margin, "header", 0);
|
|
1088
|
+
const BODY_H = PH - MT - num2(margin, "bottom", 4252) - num2(margin, "footer", 0);
|
|
1089
|
+
const BODY_W = PW - ML - num2(margin, "right", 8504);
|
|
1090
|
+
if (!hasCache) reflowSection(root, styles, { BODY_W, BODY_H }, options?.reflowMode ?? "keep");
|
|
1091
|
+
const colPr = findFirst(root, "colPr");
|
|
1092
|
+
const multiCol = num2(colPr, "colCount", 1) > 1;
|
|
1093
|
+
const paraSegPages = /* @__PURE__ */ new Map();
|
|
1094
|
+
let nPages = 1;
|
|
1095
|
+
let maxTopV = 0;
|
|
1096
|
+
{
|
|
1097
|
+
let prevV = Number.NEGATIVE_INFINITY;
|
|
1098
|
+
let prevH = Number.NEGATIVE_INFINITY;
|
|
1099
|
+
let cur = 0;
|
|
1100
|
+
for (const p of elements2(root)) {
|
|
1101
|
+
if (ln2(p) !== "p") continue;
|
|
1102
|
+
const lsa = findChildByLocalName(p, "linesegarray");
|
|
1103
|
+
const segEls = lsa ? elements2(lsa).filter((s) => ln2(s) === "lineseg") : [];
|
|
1104
|
+
const pagesOf = [];
|
|
1105
|
+
for (const s of segEls) {
|
|
1106
|
+
const v = num2(s, "vertpos");
|
|
1107
|
+
const h = num2(s, "horzpos");
|
|
1108
|
+
if (v < prevV && (!multiCol || h <= prevH)) cur++;
|
|
1109
|
+
pagesOf.push(cur);
|
|
1110
|
+
maxTopV = Math.max(maxTopV, v + num2(s, "textheight", 1e3));
|
|
1111
|
+
prevV = v;
|
|
1112
|
+
prevH = h;
|
|
1113
|
+
}
|
|
1114
|
+
paraSegPages.set(p, pagesOf);
|
|
1115
|
+
nPages = Math.max(nPages, cur + 1);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
896
1118
|
const ctx = {
|
|
897
|
-
|
|
1119
|
+
pages: Array.from({ length: nPages }, () => []),
|
|
1120
|
+
page: 0,
|
|
898
1121
|
geom: { PW, PH, ML, MT, BODY_W, BODY_H },
|
|
899
1122
|
styles,
|
|
900
1123
|
images,
|
|
1124
|
+
defs: [],
|
|
1125
|
+
highlights: (options?.highlights ?? []).map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0),
|
|
901
1126
|
warnings,
|
|
902
1127
|
warned: /* @__PURE__ */ new Set(),
|
|
903
1128
|
stats: { texts: 0, images: 0, tables: 0 }
|
|
904
1129
|
};
|
|
905
|
-
for (const p of
|
|
906
|
-
if (
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
${
|
|
1130
|
+
for (const p of elements2(root)) {
|
|
1131
|
+
if (ln2(p) !== "p") continue;
|
|
1132
|
+
drawPara(p, ML, MT, BODY_W, ctx, 0, paraSegPages.get(p));
|
|
1133
|
+
}
|
|
1134
|
+
const pageH = nPages === 1 ? Math.max(PH, MT + maxTopV + 2e3) : PH;
|
|
1135
|
+
const GAP = 2400;
|
|
1136
|
+
const totalH = nPages * pageH + (nPages - 1) * GAP;
|
|
1137
|
+
const pagesSvg = ctx.pages.map(
|
|
1138
|
+
(buf, k) => `<g data-page="${k + 1}" transform="translate(0 ${pt(k * (pageH + GAP))})"><rect width="${pt(PW)}" height="${pt(pageH)}" fill="white" stroke="#c9c7c4" stroke-width="0.75"/><g clip-path="url(#pgclip)">
|
|
1139
|
+
${buf.join("\n")}
|
|
1140
|
+
</g></g>`
|
|
1141
|
+
).join("\n");
|
|
1142
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pt(PW)} ${pt(totalH)}" width="${pt(PW)}pt" height="${pt(totalH)}pt" font-family="'HCR Batang','\uD568\uCD08\uB86C\uBC14\uD0D5','Hancom Batang',AppleMyungjo,'Noto Serif CJK KR',serif" xml:space="preserve">
|
|
1143
|
+
<defs><clipPath id="pgclip"><rect x="0" y="0" width="${pt(PW)}" height="${pt(pageH)}"/></clipPath>${ctx.defs.join("")}</defs>
|
|
1144
|
+
${pagesSvg}
|
|
915
1145
|
</svg>`;
|
|
916
|
-
return { svg, width: Math.round(PW) / 100, height: Math.round(
|
|
1146
|
+
return { svg, width: Math.round(PW) / 100, height: Math.round(totalH) / 100, pageCount: nPages, warnings, stats: ctx.stats };
|
|
917
1147
|
}
|
|
918
1148
|
|
|
919
1149
|
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
1150
|
charWidthEm1000,
|
|
930
1151
|
SPACE_EM_FIXED,
|
|
931
1152
|
SPACE_EM_FONT,
|
|
@@ -935,4 +1156,4 @@ export {
|
|
|
935
1156
|
fitRatioForFewerLines,
|
|
936
1157
|
renderHwpxToSvg
|
|
937
1158
|
};
|
|
938
|
-
//# sourceMappingURL=chunk-
|
|
1159
|
+
//# sourceMappingURL=chunk-5RHRQ7HK.js.map
|