kordoc 3.12.0 → 3.15.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 +21 -1
- package/dist/{-OPZIF3QT.js → -EYPKRLMN.js} +13 -7
- package/dist/{chunk-ILR7GF3W.js → chunk-734GJLXH.js} +522 -135
- package/dist/chunk-734GJLXH.js.map +1 -0
- package/dist/chunk-DCZVOIEO.cjs.map +1 -1
- package/dist/{chunk-2C5GCXPJ.cjs → chunk-DMBTOJK4.cjs} +5 -5
- package/dist/chunk-DMBTOJK4.cjs.map +1 -0
- package/dist/chunk-GS7T56RP.cjs.map +1 -1
- package/dist/{chunk-RUYSNNYZ.js → chunk-Q3IHI3E6.js} +325 -29
- package/dist/chunk-Q3IHI3E6.js.map +1 -0
- package/dist/{chunk-BUDM6ULT.js → chunk-Q5ZPWQID.js} +5 -5
- package/dist/{chunk-BUDM6ULT.js.map → chunk-Q5ZPWQID.js.map} +1 -1
- package/dist/{chunk-7Z43WEML.js → chunk-SCFFABCT.js} +2 -2
- package/dist/{chunk-NGKIAZ6G.js → chunk-ZRNYYEE6.js} +5 -5
- package/dist/{chunk-NGKIAZ6G.js.map → chunk-ZRNYYEE6.js.map} +1 -1
- package/dist/cli.js +71 -10
- package/dist/cli.js.map +1 -1
- package/dist/formula-5NKVS2LR.cjs.map +1 -1
- package/dist/index.cjs +1018 -335
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -6
- package/dist/index.d.ts +80 -6
- package/dist/index.js +897 -214
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +33 -10
- package/dist/mcp.js.map +1 -1
- package/dist/page-range-P7SDW6LR.cjs.map +1 -1
- package/dist/{parser-5FSEFR3R.js → parser-76JSTEP3.js} +107 -7
- package/dist/parser-76JSTEP3.js.map +1 -0
- package/dist/{parser-PCTX7LGN.cjs → parser-O2SD4DFR.cjs} +118 -18
- package/dist/parser-O2SD4DFR.cjs.map +1 -0
- package/dist/{parser-AWEGMHHV.js → parser-SNBQZO5E.js} +106 -6
- package/dist/parser-SNBQZO5E.js.map +1 -0
- package/dist/provider-G4C2V2PD.cjs.map +1 -1
- package/dist/render-LDI3LO2P.js +9 -0
- package/dist/{watch-Y7BAYXJE.js → watch-LEK5U5P6.js} +5 -5
- package/package.json +4 -3
- package/dist/chunk-2C5GCXPJ.cjs.map +0 -1
- package/dist/chunk-ILR7GF3W.js.map +0 -1
- package/dist/chunk-RUYSNNYZ.js.map +0 -1
- package/dist/parser-5FSEFR3R.js.map +0 -1
- package/dist/parser-AWEGMHHV.js.map +0 -1
- package/dist/parser-PCTX7LGN.cjs.map +0 -1
- package/dist/render-ATQNKOT2.js +0 -9
- /package/dist/{-OPZIF3QT.js.map → -EYPKRLMN.js.map} +0 -0
- /package/dist/{chunk-7Z43WEML.js.map → chunk-SCFFABCT.js.map} +0 -0
- /package/dist/{render-ATQNKOT2.js.map → render-LDI3LO2P.js.map} +0 -0
- /package/dist/{watch-Y7BAYXJE.js.map → watch-LEK5U5P6.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
KordocError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SCFFABCT.js";
|
|
5
5
|
|
|
6
6
|
// src/render/svg-render.ts
|
|
7
7
|
import JSZip from "jszip";
|
|
@@ -371,6 +371,15 @@ function fitRatioForFewerLines(text, firstWidth, contWidth, height, baseRatio, m
|
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
// src/render/head-styles.ts
|
|
374
|
+
var DEFAULT_PARA_GEOM = {
|
|
375
|
+
lineSpacingType: "PERCENT",
|
|
376
|
+
lineSpacingValue: 160,
|
|
377
|
+
marginLeft: 0,
|
|
378
|
+
marginRight: 0,
|
|
379
|
+
marginIntent: 0,
|
|
380
|
+
spaceBefore: 0,
|
|
381
|
+
spaceAfter: 0
|
|
382
|
+
};
|
|
374
383
|
var DEFAULT_CHAR = { height: 1e3, bold: false, italic: false, underline: false, ratio: 100, spacing: 0 };
|
|
375
384
|
function borderWidthPt(v) {
|
|
376
385
|
const n = parseFloat(v ?? "");
|
|
@@ -383,8 +392,43 @@ function parseEdge(el) {
|
|
|
383
392
|
if (type === "NONE") return void 0;
|
|
384
393
|
return { type, widthPt: borderWidthPt(el.getAttribute("width")), color: el.getAttribute("color") ?? "#000000" };
|
|
385
394
|
}
|
|
395
|
+
function findDeep(el, name, depth = 0) {
|
|
396
|
+
if (depth > 32) return null;
|
|
397
|
+
const children = el.childNodes;
|
|
398
|
+
if (!children) return null;
|
|
399
|
+
for (let i = 0; i < children.length; i++) {
|
|
400
|
+
const ch = children[i];
|
|
401
|
+
if (ch.nodeType !== 1) continue;
|
|
402
|
+
const e = ch;
|
|
403
|
+
if ((e.tagName || "").replace(/^[^:]+:/, "") === name) return e;
|
|
404
|
+
const found = findDeep(e, name, depth + 1);
|
|
405
|
+
if (found) return found;
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
function parseParaGeom(el) {
|
|
410
|
+
const g = { ...DEFAULT_PARA_GEOM };
|
|
411
|
+
const ls = findDeep(el, "lineSpacing");
|
|
412
|
+
if (ls) {
|
|
413
|
+
g.lineSpacingType = ls.getAttribute("type") ?? "PERCENT";
|
|
414
|
+
g.lineSpacingValue = Number(ls.getAttribute("value")) || 160;
|
|
415
|
+
}
|
|
416
|
+
const margin = findDeep(el, "margin");
|
|
417
|
+
if (margin) {
|
|
418
|
+
const v = (n) => {
|
|
419
|
+
const c = findDeep(margin, n);
|
|
420
|
+
return c ? Number(c.getAttribute("value")) || 0 : 0;
|
|
421
|
+
};
|
|
422
|
+
g.marginLeft = v("left");
|
|
423
|
+
g.marginRight = v("right");
|
|
424
|
+
g.marginIntent = v("intent");
|
|
425
|
+
g.spaceBefore = v("prev");
|
|
426
|
+
g.spaceAfter = v("next");
|
|
427
|
+
}
|
|
428
|
+
return g;
|
|
429
|
+
}
|
|
386
430
|
function parseRenderStyles(headXml) {
|
|
387
|
-
const styles = { charPr: /* @__PURE__ */ new Map(), paraAlign: /* @__PURE__ */ new Map(), borderFill: /* @__PURE__ */ new Map() };
|
|
431
|
+
const styles = { charPr: /* @__PURE__ */ new Map(), paraAlign: /* @__PURE__ */ new Map(), paraGeom: /* @__PURE__ */ new Map(), borderFill: /* @__PURE__ */ new Map() };
|
|
388
432
|
const doc = createXmlParser().parseFromString(headXml, "text/xml");
|
|
389
433
|
const root = doc.documentElement;
|
|
390
434
|
if (!root) return styles;
|
|
@@ -412,6 +456,7 @@ function parseRenderStyles(headXml) {
|
|
|
412
456
|
if (id != null) {
|
|
413
457
|
const align = findChildByLocalName(el, "align");
|
|
414
458
|
styles.paraAlign.set(id, align?.getAttribute("horizontal") || "JUSTIFY");
|
|
459
|
+
styles.paraGeom.set(id, parseParaGeom(el));
|
|
415
460
|
}
|
|
416
461
|
} else if (tag === "borderFill") {
|
|
417
462
|
const id = el.getAttribute("id");
|
|
@@ -439,11 +484,162 @@ function parseRenderStyles(headXml) {
|
|
|
439
484
|
return styles;
|
|
440
485
|
}
|
|
441
486
|
|
|
442
|
-
// src/render/
|
|
487
|
+
// src/render/reflow.ts
|
|
488
|
+
var LINESEG_FLAGS = "393216";
|
|
489
|
+
var BASELINE_RATIO = 0.85;
|
|
443
490
|
function ln(el) {
|
|
444
491
|
return (el.tagName || "").replace(/^[^:]+:/, "");
|
|
445
492
|
}
|
|
446
493
|
function elements(el) {
|
|
494
|
+
const out = [];
|
|
495
|
+
const kids = el.childNodes;
|
|
496
|
+
if (!kids) return out;
|
|
497
|
+
for (let i = 0; i < kids.length; i++) if (kids[i].nodeType === 1) out.push(kids[i]);
|
|
498
|
+
return out;
|
|
499
|
+
}
|
|
500
|
+
function num(el, attr, fallback = 0) {
|
|
501
|
+
return el ? toInt32(el.getAttribute(attr) ?? void 0, fallback) : fallback;
|
|
502
|
+
}
|
|
503
|
+
function shiftParaVert(p, delta) {
|
|
504
|
+
for (const lsa of elements(p)) {
|
|
505
|
+
if (ln(lsa) !== "linesegarray") continue;
|
|
506
|
+
for (const seg of elements(lsa)) {
|
|
507
|
+
if (ln(seg) !== "lineseg") continue;
|
|
508
|
+
seg.setAttribute("vertpos", String(num(seg, "vertpos") + delta));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function pitchFor(height, geom) {
|
|
513
|
+
const v = geom.lineSpacingValue;
|
|
514
|
+
switch (geom.lineSpacingType) {
|
|
515
|
+
case "PERCENT":
|
|
516
|
+
return Math.round(height * v / 100);
|
|
517
|
+
case "FIXED":
|
|
518
|
+
return v > 0 ? v : Math.round(height * 1.6);
|
|
519
|
+
// 고정 줄높이(HWPUNIT)
|
|
520
|
+
case "AT_LEAST":
|
|
521
|
+
return Math.max(v, height);
|
|
522
|
+
default:
|
|
523
|
+
return Math.round(height * 1.6);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function reflowPara(p, doc, styles, areaW, startV, mode) {
|
|
527
|
+
const m = buildPara(p);
|
|
528
|
+
if (m.segs.length > 0) return null;
|
|
529
|
+
const realIdx = [];
|
|
530
|
+
let text = "";
|
|
531
|
+
for (let i = 0; i < m.chars.length; i++) {
|
|
532
|
+
const ch = m.chars[i].ch;
|
|
533
|
+
if (ch === "") continue;
|
|
534
|
+
for (let u = 0; u < ch.length; u++) realIdx.push(i);
|
|
535
|
+
text += ch;
|
|
536
|
+
}
|
|
537
|
+
const geom = styles.paraGeom.get(m.paraPrId ?? "") ?? DEFAULT_PARA_GEOM;
|
|
538
|
+
let domChar = DEFAULT_CHAR;
|
|
539
|
+
for (const c of m.chars) {
|
|
540
|
+
if (c.ch !== "" && c.prId != null) {
|
|
541
|
+
const st = styles.charPr.get(c.prId);
|
|
542
|
+
if (st) {
|
|
543
|
+
domChar = st;
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
const height = domChar.height || 1e3;
|
|
549
|
+
const ratio = domChar.ratio || 100;
|
|
550
|
+
const spacingPct = domChar.spacing || 0;
|
|
551
|
+
const marginL = geom.marginLeft;
|
|
552
|
+
const avail = Math.max(1e3, areaW - marginL - geom.marginRight);
|
|
553
|
+
const firstWidth = avail;
|
|
554
|
+
const contWidth = Math.max(500, avail + Math.min(0, geom.marginIntent));
|
|
555
|
+
const contHorz = marginL - Math.min(0, geom.marginIntent);
|
|
556
|
+
const wrap = text.length === 0 ? { lines: 1, starts: [0], lastLineWidth: 0 } : simulateWrap(text, firstWidth, contWidth, height, ratio, mode, { spacingPct });
|
|
557
|
+
const pitch = pitchFor(height, geom);
|
|
558
|
+
const baseline = Math.round(height * BASELINE_RATIO);
|
|
559
|
+
const spacing = Math.max(0, pitch - height);
|
|
560
|
+
const lsa = doc.createElement("hp:linesegarray");
|
|
561
|
+
for (let li = 0; li < wrap.starts.length; li++) {
|
|
562
|
+
const startReal = wrap.starts[li];
|
|
563
|
+
const textpos = startReal < realIdx.length ? realIdx[startReal] : m.chars.length;
|
|
564
|
+
const vertpos = startV + li * pitch;
|
|
565
|
+
const isFirst = li === 0;
|
|
566
|
+
const seg = doc.createElement("hp:lineseg");
|
|
567
|
+
seg.setAttribute("textpos", String(textpos));
|
|
568
|
+
seg.setAttribute("vertpos", String(vertpos));
|
|
569
|
+
seg.setAttribute("vertsize", String(height));
|
|
570
|
+
seg.setAttribute("textheight", String(height));
|
|
571
|
+
seg.setAttribute("baseline", String(baseline));
|
|
572
|
+
seg.setAttribute("spacing", String(spacing));
|
|
573
|
+
seg.setAttribute("horzpos", String(isFirst ? marginL : contHorz));
|
|
574
|
+
seg.setAttribute("horzsize", String(isFirst ? firstWidth : contWidth));
|
|
575
|
+
seg.setAttribute("flags", LINESEG_FLAGS);
|
|
576
|
+
lsa.appendChild(seg);
|
|
577
|
+
}
|
|
578
|
+
p.appendChild(lsa);
|
|
579
|
+
const textBottom = startV + wrap.starts.length * pitch;
|
|
580
|
+
let objBottom = startV;
|
|
581
|
+
for (const o of m.objs) {
|
|
582
|
+
const h = o.tag === "tbl" ? Math.max(o.height, measureTableHeight(o.el)) : o.height;
|
|
583
|
+
objBottom = Math.max(objBottom, startV + h);
|
|
584
|
+
}
|
|
585
|
+
return { paraBottom: Math.max(textBottom, objBottom), spaceAfter: geom.spaceAfter };
|
|
586
|
+
}
|
|
587
|
+
function reflowTablesIn(p, doc, styles, mode, counter) {
|
|
588
|
+
for (const run of elements(p)) {
|
|
589
|
+
if (ln(run) !== "run") continue;
|
|
590
|
+
for (const obj of elements(run)) {
|
|
591
|
+
if (ln(obj) !== "tbl") continue;
|
|
592
|
+
for (const tr of elements(obj)) {
|
|
593
|
+
if (ln(tr) !== "tr") continue;
|
|
594
|
+
for (const tc of elements(tr)) {
|
|
595
|
+
if (ln(tc) !== "tc") continue;
|
|
596
|
+
const csz = findChildByLocalName(tc, "cellSz");
|
|
597
|
+
const cm = findChildByLocalName(tc, "cellMargin");
|
|
598
|
+
const cellW = num(csz, "width");
|
|
599
|
+
const mL = cm ? num(cm, "left", 141) : 141;
|
|
600
|
+
const mR = cm ? num(cm, "right", 141) : 141;
|
|
601
|
+
const areaW = Math.max(500, cellW - mL - mR);
|
|
602
|
+
const sub = findChildByLocalName(tc, "subList");
|
|
603
|
+
if (sub) reflowBlockFlow(sub, doc, styles, areaW, mode, counter, 0);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
function reflowBlockFlow(container, doc, styles, areaW, mode, counter, bodyH) {
|
|
610
|
+
let cursorV = 0;
|
|
611
|
+
let prevSpaceAfter = 0;
|
|
612
|
+
for (const p of elements(container)) {
|
|
613
|
+
if (ln(p) !== "p") continue;
|
|
614
|
+
reflowTablesIn(p, doc, styles, mode, counter);
|
|
615
|
+
const g = styles.paraGeom.get(p.getAttribute("paraPrIDRef") ?? "");
|
|
616
|
+
const startV = cursorV + prevSpaceAfter + (g?.spaceBefore ?? 0);
|
|
617
|
+
const res = reflowPara(p, doc, styles, areaW, startV, mode);
|
|
618
|
+
if (res) {
|
|
619
|
+
const paraH = res.paraBottom - startV;
|
|
620
|
+
if (bodyH > 0 && startV > 0 && res.paraBottom > bodyH && paraH <= bodyH) {
|
|
621
|
+
shiftParaVert(p, -startV);
|
|
622
|
+
cursorV = paraH;
|
|
623
|
+
} else {
|
|
624
|
+
cursorV = res.paraBottom;
|
|
625
|
+
}
|
|
626
|
+
prevSpaceAfter = res.spaceAfter;
|
|
627
|
+
counter.n++;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function reflowSection(root, styles, geom, mode = "keep") {
|
|
632
|
+
const doc = root.ownerDocument;
|
|
633
|
+
const counter = { n: 0 };
|
|
634
|
+
reflowBlockFlow(root, doc, styles, geom.BODY_W, mode, counter, geom.BODY_H);
|
|
635
|
+
return counter.n;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/render/svg-render.ts
|
|
639
|
+
function ln2(el) {
|
|
640
|
+
return (el.tagName || "").replace(/^[^:]+:/, "");
|
|
641
|
+
}
|
|
642
|
+
function elements2(el) {
|
|
447
643
|
const out = [];
|
|
448
644
|
const children = el.childNodes;
|
|
449
645
|
if (!children) return out;
|
|
@@ -452,13 +648,13 @@ function elements(el) {
|
|
|
452
648
|
}
|
|
453
649
|
return out;
|
|
454
650
|
}
|
|
455
|
-
function
|
|
651
|
+
function num2(el, attr, fallback = 0) {
|
|
456
652
|
return el ? toInt32(el.getAttribute(attr) ?? void 0, fallback) : fallback;
|
|
457
653
|
}
|
|
458
654
|
function findFirst(el, name, depth = 0) {
|
|
459
655
|
if (depth > 64) return null;
|
|
460
|
-
for (const ch of
|
|
461
|
-
if (
|
|
656
|
+
for (const ch of elements2(el)) {
|
|
657
|
+
if (ln2(ch) === name) return ch;
|
|
462
658
|
const found = findFirst(ch, name, depth + 1);
|
|
463
659
|
if (found) return found;
|
|
464
660
|
}
|
|
@@ -469,59 +665,80 @@ function escapeXml(s) {
|
|
|
469
665
|
}
|
|
470
666
|
var OBJ_TAGS = /* @__PURE__ */ new Set(["tbl", "pic", "container", "equation", "rect", "ellipse", "polygon", "curv", "line", "arc", "ole", "textart"]);
|
|
471
667
|
var pt = (u) => String(Math.round(u) / 100);
|
|
668
|
+
function emit(ctx, s) {
|
|
669
|
+
ctx.pages[ctx.page].push(s);
|
|
670
|
+
}
|
|
472
671
|
function warnOnce(ctx, key, msg) {
|
|
473
672
|
if (ctx.warned.has(key)) return;
|
|
474
673
|
ctx.warned.add(key);
|
|
475
674
|
ctx.warnings.push(msg);
|
|
476
675
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
676
|
+
var CHAR_CTRL_1SLOT = /* @__PURE__ */ new Set(["lineBreak", "hyphen", "nbSpace", "fwSpace"]);
|
|
677
|
+
function pushFillers(chars, n, prId) {
|
|
678
|
+
for (let i = 0; i < n; i++) chars.push({ ch: "", prId });
|
|
679
|
+
}
|
|
680
|
+
function pushTextSlots(t, chars, prId, depth) {
|
|
681
|
+
if (depth > 32) return;
|
|
682
|
+
const kids = t.childNodes;
|
|
683
|
+
if (!kids) return;
|
|
684
|
+
for (let i = 0; i < kids.length; i++) {
|
|
685
|
+
const c = kids[i];
|
|
686
|
+
if (c.nodeType === 3) {
|
|
687
|
+
for (const cp of c.textContent ?? "") {
|
|
688
|
+
chars.push({ ch: cp, prId });
|
|
689
|
+
if (cp.length === 2) chars.push({ ch: "", prId });
|
|
690
|
+
}
|
|
691
|
+
} else if (c.nodeType === 1) {
|
|
692
|
+
const el = c;
|
|
693
|
+
const tag = ln2(el);
|
|
694
|
+
if (tag === "tab") {
|
|
695
|
+
pushFillers(chars, 8, prId);
|
|
696
|
+
} else if (CHAR_CTRL_1SLOT.has(tag)) {
|
|
697
|
+
chars.push({ ch: tag === "nbSpace" || tag === "fwSpace" ? " " : "", prId });
|
|
698
|
+
} else {
|
|
699
|
+
pushTextSlots(el, chars, prId, depth + 1);
|
|
700
|
+
}
|
|
487
701
|
}
|
|
488
|
-
}
|
|
489
|
-
walk(t, 0);
|
|
490
|
-
return s;
|
|
702
|
+
}
|
|
491
703
|
}
|
|
492
704
|
function buildPara(p) {
|
|
493
705
|
const chars = [];
|
|
494
706
|
const objs = [];
|
|
495
707
|
let segs = [];
|
|
496
|
-
for (const runEl of
|
|
497
|
-
const tag =
|
|
708
|
+
for (const runEl of elements2(p)) {
|
|
709
|
+
const tag = ln2(runEl);
|
|
498
710
|
if (tag === "run") {
|
|
499
711
|
const prId = runEl.getAttribute("charPrIDRef");
|
|
500
|
-
for (const ch of
|
|
501
|
-
const cn =
|
|
712
|
+
for (const ch of elements2(runEl)) {
|
|
713
|
+
const cn = ln2(ch);
|
|
502
714
|
if (cn === "t") {
|
|
503
|
-
|
|
715
|
+
pushTextSlots(ch, chars, prId, 0);
|
|
504
716
|
} else if (OBJ_TAGS.has(cn)) {
|
|
505
717
|
const sz = findChildByLocalName(ch, "sz");
|
|
506
718
|
const pos = findChildByLocalName(ch, "pos");
|
|
719
|
+
const w = num2(sz, "width") || num2(findChildByLocalName(ch, "curSz"), "width") || num2(findChildByLocalName(ch, "orgSz"), "width");
|
|
720
|
+
const h = num2(sz, "height") || num2(findChildByLocalName(ch, "curSz"), "height") || num2(findChildByLocalName(ch, "orgSz"), "height");
|
|
507
721
|
objs.push({
|
|
508
722
|
el: ch,
|
|
509
723
|
tag: cn,
|
|
510
724
|
index: chars.length,
|
|
511
725
|
inline: pos?.getAttribute("treatAsChar") === "1",
|
|
512
|
-
width:
|
|
513
|
-
height:
|
|
726
|
+
width: w,
|
|
727
|
+
height: h
|
|
514
728
|
});
|
|
729
|
+
pushFillers(chars, 8, prId);
|
|
730
|
+
} else {
|
|
731
|
+
pushFillers(chars, 8, prId);
|
|
515
732
|
}
|
|
516
733
|
}
|
|
517
734
|
} else if (tag === "linesegarray") {
|
|
518
|
-
segs =
|
|
519
|
-
textpos:
|
|
520
|
-
vertpos:
|
|
521
|
-
horzpos:
|
|
522
|
-
horzsize:
|
|
523
|
-
textheight:
|
|
524
|
-
baseline:
|
|
735
|
+
segs = elements2(runEl).filter((s) => ln2(s) === "lineseg").map((s) => ({
|
|
736
|
+
textpos: num2(s, "textpos"),
|
|
737
|
+
vertpos: num2(s, "vertpos"),
|
|
738
|
+
horzpos: num2(s, "horzpos"),
|
|
739
|
+
horzsize: num2(s, "horzsize"),
|
|
740
|
+
textheight: num2(s, "textheight", 1e3),
|
|
741
|
+
baseline: num2(s, "baseline", 850)
|
|
525
742
|
}));
|
|
526
743
|
}
|
|
527
744
|
}
|
|
@@ -569,38 +786,41 @@ function advanceTo(m, styles, plan, upto) {
|
|
|
569
786
|
for (const o of m.objs) if (o.inline && o.index >= plan.start && o.index < upto) x += o.width;
|
|
570
787
|
return x;
|
|
571
788
|
}
|
|
572
|
-
function drawPara(p, ox, oy, areaW, ctx, depth) {
|
|
789
|
+
function drawPara(p, ox, oy, areaW, ctx, depth, segPages) {
|
|
573
790
|
if (depth > 16) {
|
|
574
791
|
warnOnce(ctx, "depth", "\uC911\uCCA9 \uAE4A\uC774 16 \uCD08\uACFC \u2014 \uC774\uD558 \uC0DD\uB7B5");
|
|
575
792
|
return;
|
|
576
793
|
}
|
|
577
794
|
const m = buildPara(p);
|
|
578
795
|
if (m.segs.length === 0) {
|
|
796
|
+
if (m.chars.some((c) => c.ch !== "")) {
|
|
797
|
+
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");
|
|
798
|
+
}
|
|
579
799
|
for (const o of m.objs) drawObject(o, ox, oy, 0, areaW, ctx, depth);
|
|
580
800
|
return;
|
|
581
801
|
}
|
|
582
802
|
const plans = planLines(m, ctx.styles);
|
|
583
803
|
const baseV = m.segs[0].vertpos;
|
|
584
|
-
for (
|
|
804
|
+
for (let li = 0; li < plans.length; li++) {
|
|
805
|
+
const plan = plans[li];
|
|
806
|
+
if (segPages && segPages[li] !== void 0) ctx.page = segPages[li];
|
|
585
807
|
const { seg } = plan;
|
|
586
808
|
let i = plan.start;
|
|
587
809
|
let cursor = ox + seg.horzpos + plan.xoff;
|
|
588
810
|
const y = oy + seg.vertpos + seg.baseline;
|
|
589
811
|
while (i < plan.end && i < m.chars.length) {
|
|
812
|
+
if (m.chars[i].ch === "") {
|
|
813
|
+
for (const o of m.objs) if (o.inline && o.index === i) cursor += o.width;
|
|
814
|
+
i++;
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
590
817
|
const prId = m.chars[i].prId;
|
|
591
818
|
let j = i;
|
|
592
819
|
let piece = "";
|
|
593
|
-
while (j < plan.end && j < m.chars.length && m.chars[j].prId === prId) {
|
|
820
|
+
while (j < plan.end && j < m.chars.length && m.chars[j].prId === prId && m.chars[j].ch !== "") {
|
|
594
821
|
piece += m.chars[j].ch;
|
|
595
822
|
j++;
|
|
596
823
|
}
|
|
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
824
|
{
|
|
605
825
|
const cut = piece.search(/ {2,}/);
|
|
606
826
|
if (cut > 0) {
|
|
@@ -613,32 +833,71 @@ function drawPara(p, ox, oy, areaW, ctx, depth) {
|
|
|
613
833
|
}
|
|
614
834
|
}
|
|
615
835
|
const st = (prId != null ? ctx.styles.charPr.get(prId) : void 0) ?? DEFAULT_CHAR;
|
|
616
|
-
const
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
836
|
+
const renderSeg = (text, cx, hit) => {
|
|
837
|
+
const sw = measureTextWidth(text, st.height, st.ratio, { spacingPct: st.spacing }) * plan.scale;
|
|
838
|
+
if (hit) {
|
|
839
|
+
emit(ctx, `<rect x="${pt(cx)}" y="${pt(oy + seg.vertpos)}" width="${pt(sw)}" height="${pt(seg.textheight)}" fill="#ffd54f" fill-opacity="0.45"/>`);
|
|
840
|
+
}
|
|
841
|
+
if (text.trim().length > 0) {
|
|
842
|
+
const attrs = [`x="${pt(cx)}"`, `y="${pt(y)}"`, `font-size="${pt(st.height)}"`];
|
|
843
|
+
if ([...text].length > 1 && sw > 50) {
|
|
844
|
+
attrs.push(`textLength="${pt(sw)}"`, `lengthAdjust="${plan.scale < 1 ? "spacingAndGlyphs" : "spacing"}"`);
|
|
845
|
+
}
|
|
846
|
+
if (st.bold) attrs.push(`font-weight="bold"`);
|
|
847
|
+
if (st.italic) attrs.push(`font-style="italic"`);
|
|
848
|
+
if (st.underline) attrs.push(`text-decoration="underline"`);
|
|
849
|
+
if (st.color) attrs.push(`fill="${escapeXml(st.color)}"`);
|
|
850
|
+
emit(ctx, `<text ${attrs.join(" ")}>${escapeXml(text)}</text>`);
|
|
851
|
+
ctx.stats.texts++;
|
|
852
|
+
}
|
|
853
|
+
return sw;
|
|
854
|
+
};
|
|
855
|
+
const merged = [];
|
|
856
|
+
if (ctx.highlights.length > 0 && piece.trim().length > 0) {
|
|
857
|
+
const found = [];
|
|
858
|
+
const lower = piece.toLowerCase();
|
|
859
|
+
for (const term of ctx.highlights) {
|
|
860
|
+
for (let f = lower.indexOf(term); f !== -1; f = lower.indexOf(term, f + term.length)) {
|
|
861
|
+
found.push([f, f + term.length]);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
found.sort((a, b) => a[0] - b[0]);
|
|
865
|
+
for (const [s, e] of found) {
|
|
866
|
+
const tail = merged[merged.length - 1];
|
|
867
|
+
if (tail && s <= tail[1]) tail[1] = Math.max(tail[1], e);
|
|
868
|
+
else merged.push([s, e]);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
if (merged.length === 0) {
|
|
872
|
+
cursor += renderSeg(piece, cursor, false);
|
|
873
|
+
} else {
|
|
874
|
+
let segCur = cursor;
|
|
875
|
+
let last = 0;
|
|
876
|
+
for (const [s, e] of merged) {
|
|
877
|
+
segCur += renderSeg(piece.slice(last, s), segCur, false);
|
|
878
|
+
segCur += renderSeg(piece.slice(s, e), segCur, true);
|
|
879
|
+
last = e;
|
|
621
880
|
}
|
|
622
|
-
|
|
623
|
-
|
|
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++;
|
|
881
|
+
segCur += renderSeg(piece.slice(last), segCur, false);
|
|
882
|
+
cursor = segCur;
|
|
628
883
|
}
|
|
629
|
-
cursor += w;
|
|
630
|
-
for (const o of m.objs) if (o.inline && o.index === j) cursor += o.width;
|
|
631
884
|
i = j;
|
|
632
885
|
}
|
|
633
886
|
}
|
|
634
887
|
for (const o of m.objs) {
|
|
635
888
|
if (o.inline) {
|
|
636
|
-
let
|
|
637
|
-
for (
|
|
889
|
+
let planIdx = 0;
|
|
890
|
+
for (let k = 0; k < plans.length; k++) {
|
|
891
|
+
const pl = plans[k];
|
|
892
|
+
if (pl.start <= o.index && (o.index < pl.end || k === plans.length - 1)) planIdx = k;
|
|
893
|
+
}
|
|
894
|
+
const plan = plans[planIdx];
|
|
895
|
+
if (segPages && segPages[planIdx] !== void 0) ctx.page = segPages[planIdx];
|
|
638
896
|
const x = ox + plan.seg.horzpos + plan.xoff + advanceTo(m, ctx.styles, plan, o.index);
|
|
639
897
|
const yTop = oy + plan.seg.vertpos + Math.max(0, plan.seg.baseline - o.height);
|
|
640
898
|
drawObject(o, x, yTop, baseV, areaW, ctx, depth);
|
|
641
899
|
} else {
|
|
900
|
+
if (segPages && segPages[0] !== void 0) ctx.page = segPages[0];
|
|
642
901
|
const { x, y } = anchorObject(o, ox, oy, baseV, areaW, ctx);
|
|
643
902
|
drawObject(o, x, y, baseV, areaW, ctx, depth);
|
|
644
903
|
}
|
|
@@ -648,11 +907,11 @@ function anchorObject(o, ox, oy, baseV, areaW, ctx) {
|
|
|
648
907
|
const { PW, PH, ML, MT, BODY_W, BODY_H } = ctx.geom;
|
|
649
908
|
const pos = findChildByLocalName(o.el, "pos");
|
|
650
909
|
const om = findChildByLocalName(o.el, "outMargin");
|
|
651
|
-
const omT =
|
|
910
|
+
const omT = num2(om, "top"), omB = num2(om, "bottom");
|
|
652
911
|
const w = o.width, h = o.height;
|
|
653
912
|
if (!pos) return { x: ox, y: oy + baseV };
|
|
654
|
-
const vo =
|
|
655
|
-
const ho =
|
|
913
|
+
const vo = num2(pos, "vertOffset");
|
|
914
|
+
const ho = num2(pos, "horzOffset");
|
|
656
915
|
const vrel = pos.getAttribute("vertRelTo") ?? "PARA";
|
|
657
916
|
const hrel = pos.getAttribute("horzRelTo") ?? "PARA";
|
|
658
917
|
const va = pos.getAttribute("vertAlign") ?? "TOP";
|
|
@@ -684,25 +943,74 @@ function drawObject(o, x, y, baseV, areaW, ctx, depth) {
|
|
|
684
943
|
if (o.tag === "tbl") drawTable(o.el, x, y, ctx, depth + 1);
|
|
685
944
|
else if (o.tag === "pic") drawPic(o.el, x, y, ctx);
|
|
686
945
|
else if (o.tag === "container") {
|
|
687
|
-
for (const ch of
|
|
688
|
-
const tag =
|
|
946
|
+
for (const ch of elements2(o.el)) {
|
|
947
|
+
const tag = ln2(ch);
|
|
689
948
|
if (!OBJ_TAGS.has(tag)) continue;
|
|
690
949
|
const sz = findChildByLocalName(ch, "sz");
|
|
691
950
|
const off = findChildByLocalName(ch, "offset");
|
|
692
|
-
const sub = { el: ch, tag, index: 0, inline: true, width:
|
|
693
|
-
drawObject(sub, x +
|
|
951
|
+
const sub = { el: ch, tag, index: 0, inline: true, width: num2(sz, "width"), height: num2(sz, "height") };
|
|
952
|
+
drawObject(sub, x + num2(off, "x"), y + num2(off, "y"), baseV, areaW, ctx, depth + 1);
|
|
694
953
|
}
|
|
695
954
|
} else if (o.tag === "equation") {
|
|
696
955
|
warnOnce(ctx, "equation", "\uC218\uC2DD \uAC1C\uCCB4\uB294 \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5");
|
|
956
|
+
} else if (SHAPE_TAGS.has(o.tag)) {
|
|
957
|
+
drawShape(o, x, y, ctx, depth);
|
|
697
958
|
} else {
|
|
698
|
-
warnOnce(ctx, `shape:${o.tag}`, `\
|
|
959
|
+
warnOnce(ctx, `shape:${o.tag}`, `\uAC1C\uCCB4(${o.tag}) \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5`);
|
|
699
960
|
}
|
|
700
961
|
}
|
|
701
|
-
|
|
962
|
+
var SHAPE_TAGS = /* @__PURE__ */ new Set(["rect", "ellipse", "line", "polygon", "curv", "arc"]);
|
|
963
|
+
function shapeStrokePt(v) {
|
|
964
|
+
return Math.max(0.2, v / 100 * 2.834645);
|
|
965
|
+
}
|
|
966
|
+
function drawShape(o, x, y, ctx, depth) {
|
|
967
|
+
const el = o.el;
|
|
968
|
+
const orgSz = findChildByLocalName(el, "orgSz");
|
|
969
|
+
const curSz = findChildByLocalName(el, "curSz");
|
|
970
|
+
const ow = num2(orgSz, "width"), oh = num2(orgSz, "height");
|
|
971
|
+
const w = num2(curSz, "width") || ow || o.width;
|
|
972
|
+
const h = num2(curSz, "height") || oh || o.height;
|
|
973
|
+
const sx = ow > 0 ? w / ow : 1;
|
|
974
|
+
const sy = oh > 0 ? h / oh : 1;
|
|
975
|
+
const lineShape = findChildByLocalName(el, "lineShape");
|
|
976
|
+
const lstyle = lineShape?.getAttribute("style") ?? "SOLID";
|
|
977
|
+
const strokeCol = lineShape?.getAttribute("color") || "#000000";
|
|
978
|
+
const hasStroke = lstyle !== "NONE";
|
|
979
|
+
const strokeW = hasStroke ? shapeStrokePt(lineShape ? num2(lineShape, "width") : 33) : 0;
|
|
980
|
+
const dash = /DASH|DOT/.test(lstyle) ? ` stroke-dasharray="${lstyle.includes("DOT") ? "1,1.5" : "3,1.5"}"` : "";
|
|
981
|
+
const strokeAttr = hasStroke ? ` stroke="${escapeXml(strokeCol)}" stroke-width="${strokeW.toFixed(2)}"${dash}` : "";
|
|
982
|
+
const fillBrush = findChildByLocalName(el, "fillBrush");
|
|
983
|
+
const winBrush = fillBrush ? findChildByLocalName(fillBrush, "winBrush") : null;
|
|
984
|
+
const face = winBrush?.getAttribute("faceColor");
|
|
985
|
+
const fill = face && face.toLowerCase() !== "none" ? face : "none";
|
|
986
|
+
const fillAttr = ` fill="${fill === "none" ? "none" : escapeXml(fill)}"`;
|
|
987
|
+
if (o.tag === "rect") {
|
|
988
|
+
emit(ctx, `<rect x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}"${fillAttr}${strokeAttr}/>`);
|
|
989
|
+
} else if (o.tag === "ellipse") {
|
|
990
|
+
emit(ctx, `<ellipse cx="${pt(x + w / 2)}" cy="${pt(y + h / 2)}" rx="${pt(w / 2)}" ry="${pt(h / 2)}"${fillAttr}${strokeAttr}/>`);
|
|
991
|
+
} else if (o.tag === "line") {
|
|
992
|
+
const s = findChildByLocalName(el, "startPt"), e = findChildByLocalName(el, "endPt");
|
|
993
|
+
const x1 = x + num2(s, "x") * sx, y1 = y + num2(s, "y") * sy;
|
|
994
|
+
const x2 = x + num2(e, "x") * sx, y2 = y + num2(e, "y") * sy;
|
|
995
|
+
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}/>`);
|
|
996
|
+
} else if (o.tag === "polygon" || o.tag === "curv") {
|
|
997
|
+
const pts = [];
|
|
998
|
+
for (const c of elements2(el)) if (ln2(c) === "pt") pts.push(`${pt(x + num2(c, "x") * sx)},${pt(y + num2(c, "y") * sy)}`);
|
|
999
|
+
if (pts.length >= 2) emit(ctx, `<polygon points="${pts.join(" ")}"${fillAttr}${strokeAttr}/>`);
|
|
1000
|
+
} else if (o.tag === "arc") {
|
|
1001
|
+
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"`}/>`);
|
|
1002
|
+
}
|
|
1003
|
+
const dt = findChildByLocalName(el, "drawText");
|
|
1004
|
+
const sub = dt ? findChildByLocalName(dt, "subList") : null;
|
|
1005
|
+
if (sub) {
|
|
1006
|
+
for (const p of elements2(sub)) if (ln2(p) === "p") drawPara(p, x, y, w, ctx, depth + 1);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
function cellContentExtent(cell) {
|
|
702
1010
|
if (!cell.sub) return 0;
|
|
703
1011
|
let ext = 0;
|
|
704
|
-
for (const p of
|
|
705
|
-
if (
|
|
1012
|
+
for (const p of elements2(cell.sub)) {
|
|
1013
|
+
if (ln2(p) !== "p") continue;
|
|
706
1014
|
const m = buildPara(p);
|
|
707
1015
|
for (const s of m.segs) ext = Math.max(ext, s.vertpos + s.textheight);
|
|
708
1016
|
const baseV = m.segs[0]?.vertpos ?? 0;
|
|
@@ -711,9 +1019,9 @@ function cellContentExtent(cell, ctx) {
|
|
|
711
1019
|
const pos = findChildByLocalName(o.el, "pos");
|
|
712
1020
|
if ((pos?.getAttribute("vertRelTo") ?? "PARA") !== "PARA") continue;
|
|
713
1021
|
const om = findChildByLocalName(o.el, "outMargin");
|
|
714
|
-
const pushed = baseV - (
|
|
1022
|
+
const pushed = baseV - (num2(om, "top") + o.height + num2(om, "bottom"));
|
|
715
1023
|
const anchor = pushed >= -100 ? pushed : baseV;
|
|
716
|
-
ext = Math.max(ext, anchor +
|
|
1024
|
+
ext = Math.max(ext, anchor + num2(om, "top") + num2(pos, "vertOffset") + o.height);
|
|
717
1025
|
}
|
|
718
1026
|
}
|
|
719
1027
|
return ext;
|
|
@@ -722,21 +1030,15 @@ function edgeLine(x1, y1, x2, y2, e) {
|
|
|
722
1030
|
const dash = /DASH|DOT/.test(e.type) ? ` stroke-dasharray="${e.type.includes("DOT") ? "1,1.5" : "3,1.5"}"` : "";
|
|
723
1031
|
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
1032
|
}
|
|
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");
|
|
1033
|
+
function collectCells(tbl) {
|
|
732
1034
|
const inMargin = findChildByLocalName(tbl, "inMargin");
|
|
733
|
-
const defL =
|
|
734
|
-
const defT =
|
|
1035
|
+
const defL = num2(inMargin, "left", 141), defR = num2(inMargin, "right", 141);
|
|
1036
|
+
const defT = num2(inMargin, "top", 141), defB = num2(inMargin, "bottom", 141);
|
|
735
1037
|
const cells = [];
|
|
736
|
-
for (const tr of
|
|
737
|
-
if (
|
|
738
|
-
for (const tc of
|
|
739
|
-
if (
|
|
1038
|
+
for (const tr of elements2(tbl)) {
|
|
1039
|
+
if (ln2(tr) !== "tr") continue;
|
|
1040
|
+
for (const tc of elements2(tr)) {
|
|
1041
|
+
if (ln2(tc) !== "tc") continue;
|
|
740
1042
|
const addr = findChildByLocalName(tc, "cellAddr");
|
|
741
1043
|
const span = findChildByLocalName(tc, "cellSpan");
|
|
742
1044
|
const csz = findChildByLocalName(tc, "cellSz");
|
|
@@ -744,28 +1046,50 @@ function drawTable(tbl, tx, ty, ctx, depth) {
|
|
|
744
1046
|
if (!addr || !csz) continue;
|
|
745
1047
|
cells.push({
|
|
746
1048
|
el: tc,
|
|
747
|
-
ca:
|
|
748
|
-
ra:
|
|
749
|
-
cs: Math.max(1,
|
|
750
|
-
rs: Math.max(1,
|
|
751
|
-
w:
|
|
752
|
-
h:
|
|
1049
|
+
ca: num2(addr, "colAddr"),
|
|
1050
|
+
ra: num2(addr, "rowAddr"),
|
|
1051
|
+
cs: Math.max(1, num2(span, "colSpan", 1)),
|
|
1052
|
+
rs: Math.max(1, num2(span, "rowSpan", 1)),
|
|
1053
|
+
w: num2(csz, "width"),
|
|
1054
|
+
h: num2(csz, "height"),
|
|
753
1055
|
bfId: tc.getAttribute("borderFillIDRef"),
|
|
754
1056
|
sub: findChildByLocalName(tc, "subList"),
|
|
755
|
-
marginL: cm ?
|
|
756
|
-
marginR: cm ?
|
|
757
|
-
marginT: cm ?
|
|
758
|
-
marginB: cm ?
|
|
1057
|
+
marginL: cm ? num2(cm, "left", defL) : defL,
|
|
1058
|
+
marginR: cm ? num2(cm, "right", defR) : defR,
|
|
1059
|
+
marginT: cm ? num2(cm, "top", defT) : defT,
|
|
1060
|
+
marginB: cm ? num2(cm, "bottom", defB) : defB
|
|
759
1061
|
});
|
|
760
1062
|
}
|
|
761
1063
|
}
|
|
1064
|
+
return cells;
|
|
1065
|
+
}
|
|
1066
|
+
function measureTableHeight(tbl) {
|
|
1067
|
+
const cells = collectCells(tbl);
|
|
1068
|
+
if (cells.length === 0 || cells.length > 4096) return 0;
|
|
1069
|
+
const nRows = Math.max(...cells.map((c) => c.ra + c.rs));
|
|
1070
|
+
const rowH = solveRowHeights(
|
|
1071
|
+
cells.map((c) => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c) : void 0 })),
|
|
1072
|
+
nRows
|
|
1073
|
+
);
|
|
1074
|
+
let sum = 0;
|
|
1075
|
+
for (const h of rowH) sum += h;
|
|
1076
|
+
return sum;
|
|
1077
|
+
}
|
|
1078
|
+
function drawTable(tbl, tx, ty, ctx, depth) {
|
|
1079
|
+
if (depth > 16) {
|
|
1080
|
+
warnOnce(ctx, "depth", "\uC911\uCCA9 \uAE4A\uC774 16 \uCD08\uACFC \u2014 \uC774\uD558 \uC0DD\uB7B5");
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
ctx.stats.tables++;
|
|
1084
|
+
const tblSz = findChildByLocalName(tbl, "sz");
|
|
1085
|
+
const cells = collectCells(tbl);
|
|
762
1086
|
if (cells.length === 0 || cells.length > 4096) return;
|
|
763
1087
|
const nCols = Math.max(...cells.map((c) => c.ca + c.cs));
|
|
764
1088
|
const nRows = Math.max(...cells.map((c) => c.ra + c.rs));
|
|
765
1089
|
const colCons = cells.map((c) => ({ a: c.ca, b: c.ca + c.cs, size: c.w }));
|
|
766
|
-
const colX = solveBoundaries(colCons, nCols,
|
|
1090
|
+
const colX = solveBoundaries(colCons, nCols, num2(tblSz, "width") || void 0);
|
|
767
1091
|
const rowH = solveRowHeights(
|
|
768
|
-
cells.map((c) => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c
|
|
1092
|
+
cells.map((c) => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c) : void 0 })),
|
|
769
1093
|
nRows
|
|
770
1094
|
);
|
|
771
1095
|
const rowY = [0];
|
|
@@ -779,55 +1103,69 @@ function drawTable(tbl, tx, ty, ctx, depth) {
|
|
|
779
1103
|
}));
|
|
780
1104
|
for (const g of geom) {
|
|
781
1105
|
const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : void 0;
|
|
782
|
-
if (bf?.fill) ctx
|
|
1106
|
+
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
1107
|
}
|
|
784
1108
|
for (const g of geom) {
|
|
785
1109
|
const { c } = g;
|
|
786
1110
|
if (!c.sub) continue;
|
|
787
1111
|
const innerH = g.h - c.marginT - c.marginB;
|
|
788
|
-
const extent = cellContentExtent(c
|
|
1112
|
+
const extent = cellContentExtent(c);
|
|
789
1113
|
const va = c.sub.getAttribute("vertAlign") ?? "TOP";
|
|
790
1114
|
let yoff = 0;
|
|
791
1115
|
if (va === "CENTER") yoff = Math.max(0, (innerH - extent) / 2);
|
|
792
1116
|
else if (va === "BOTTOM") yoff = Math.max(0, innerH - extent);
|
|
793
|
-
for (const p of
|
|
794
|
-
if (
|
|
1117
|
+
for (const p of elements2(c.sub)) {
|
|
1118
|
+
if (ln2(p) !== "p") continue;
|
|
795
1119
|
drawPara(p, g.x + c.marginL, g.y + c.marginT + yoff, g.w - c.marginL - c.marginR, ctx, depth + 1);
|
|
796
1120
|
}
|
|
797
1121
|
}
|
|
798
1122
|
for (const g of geom) {
|
|
799
1123
|
const bf = g.c.bfId != null ? ctx.styles.borderFill.get(g.c.bfId) : void 0;
|
|
800
1124
|
if (!bf) continue;
|
|
801
|
-
if (bf.top) ctx
|
|
802
|
-
if (bf.bottom) ctx
|
|
803
|
-
if (bf.left) ctx
|
|
804
|
-
if (bf.right) ctx
|
|
1125
|
+
if (bf.top) emit(ctx, edgeLine(g.x, g.y, g.x + g.w, g.y, bf.top));
|
|
1126
|
+
if (bf.bottom) emit(ctx, edgeLine(g.x, g.y + g.h, g.x + g.w, g.y + g.h, bf.bottom));
|
|
1127
|
+
if (bf.left) emit(ctx, edgeLine(g.x, g.y, g.x, g.y + g.h, bf.left));
|
|
1128
|
+
if (bf.right) emit(ctx, edgeLine(g.x + g.w, g.y, g.x + g.w, g.y + g.h, bf.right));
|
|
805
1129
|
}
|
|
806
1130
|
}
|
|
1131
|
+
function imageSymbol(loaded, ctx) {
|
|
1132
|
+
if (!loaded.symId) {
|
|
1133
|
+
loaded.symId = `bin${ctx.defs.length}`;
|
|
1134
|
+
ctx.defs.push(
|
|
1135
|
+
`<symbol id="${loaded.symId}" viewBox="0 0 100 100" preserveAspectRatio="none"><image width="100" height="100" preserveAspectRatio="none" href="${loaded.dataUri}"/></symbol>`
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
return loaded.symId;
|
|
1139
|
+
}
|
|
807
1140
|
function drawPic(pic, x, y, ctx) {
|
|
808
1141
|
const sz = findChildByLocalName(pic, "sz");
|
|
809
|
-
const w =
|
|
1142
|
+
const w = num2(sz, "width", 5669), h = num2(sz, "height", 5669);
|
|
810
1143
|
const img = findFirst(pic, "img");
|
|
811
1144
|
const ref = img?.getAttribute("binaryItemIDRef");
|
|
812
1145
|
const loaded = ref != null ? ctx.images.get(ref) : void 0;
|
|
813
1146
|
if (!loaded) {
|
|
814
|
-
ctx
|
|
1147
|
+
emit(ctx, `<rect x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" fill="#eee" stroke="#c00" stroke-width="0.5"/>`);
|
|
815
1148
|
warnOnce(ctx, `img:${ref}`, `\uC774\uBBF8\uC9C0 \uBC14\uC774\uB108\uB9AC \uB204\uB77D: ${ref ?? "(ref \uC5C6\uC74C)"}`);
|
|
816
1149
|
return;
|
|
817
1150
|
}
|
|
818
1151
|
ctx.stats.images++;
|
|
819
|
-
const orgSz = findChildByLocalName(pic, "orgSz");
|
|
820
1152
|
const clip = findChildByLocalName(pic, "imgClip");
|
|
821
|
-
const
|
|
822
|
-
const
|
|
823
|
-
const
|
|
824
|
-
const
|
|
1153
|
+
const imgDim = findChildByLocalName(pic, "imgDim");
|
|
1154
|
+
const orgSz = findChildByLocalName(pic, "orgSz");
|
|
1155
|
+
const dimW = num2(imgDim, "dimwidth"), dimH = num2(imgDim, "dimheight");
|
|
1156
|
+
const refW = dimW > 0 ? dimW : num2(orgSz, "width");
|
|
1157
|
+
const refH = dimH > 0 ? dimH : num2(orgSz, "height");
|
|
1158
|
+
const cl = num2(clip, "left"), ct = num2(clip, "top");
|
|
1159
|
+
const cr = num2(clip, "right", refW), cb = num2(clip, "bottom", refH);
|
|
1160
|
+
const cropped = refW > 0 && refH > 0 && clip != null && (cl > 0 || ct > 0 || cr < refW || cb < refH) && cr > cl && cb > ct;
|
|
1161
|
+
const symId = imageSymbol(loaded, ctx);
|
|
825
1162
|
if (cropped) {
|
|
826
|
-
|
|
827
|
-
|
|
1163
|
+
emit(
|
|
1164
|
+
ctx,
|
|
1165
|
+
`<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
1166
|
);
|
|
829
1167
|
} else {
|
|
830
|
-
ctx
|
|
1168
|
+
emit(ctx, `<use href="#${symId}" x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}"/>`);
|
|
831
1169
|
}
|
|
832
1170
|
}
|
|
833
1171
|
function sniffMime(name, bytes) {
|
|
@@ -850,12 +1188,13 @@ async function renderHwpxToSvg(input, options) {
|
|
|
850
1188
|
if (!secFile) throw new KordocError("Contents/section0.xml \uC5C6\uC74C \u2014 HWPX\uAC00 \uC544\uB2C8\uAC70\uB098 \uC190\uC0C1\uB428");
|
|
851
1189
|
const secXml = await secFile.async("string");
|
|
852
1190
|
if (secXml.length > MAX_DECOMPRESS_SIZE) throw new KordocError("\uC139\uC158 XML\uC774 \uD5C8\uC6A9 \uD06C\uAE30\uB97C \uCD08\uACFC");
|
|
853
|
-
|
|
854
|
-
|
|
1191
|
+
const hasCache = /<(?:[A-Za-z][\w.-]*:)?linesegarray[\s/>]/.test(secXml);
|
|
1192
|
+
if (!hasCache && !options?.reflow) {
|
|
1193
|
+
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
1194
|
}
|
|
856
1195
|
const warnings = [];
|
|
857
1196
|
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() };
|
|
1197
|
+
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
1198
|
if (!headFile) warnings.push("header.xml \uC5C6\uC74C \u2014 \uAE30\uBCF8 \uC2A4\uD0C0\uC77C\uB85C \uB80C\uB354");
|
|
860
1199
|
const binmap = /* @__PURE__ */ new Map();
|
|
861
1200
|
const hpf = zip.file(/content\.hpf$/i)[0];
|
|
@@ -864,10 +1203,17 @@ async function renderHwpxToSvg(input, options) {
|
|
|
864
1203
|
for (const m of man.matchAll(/<[^>]*\bid="([^"]+)"[^>]*\bhref="(BinData\/[^"]+)"[^>]*>/g)) binmap.set(m[1], m[2]);
|
|
865
1204
|
for (const m of man.matchAll(/<[^>]*\bhref="(BinData\/[^"]+)"[^>]*\bid="([^"]+)"[^>]*>/g)) binmap.set(m[2], m[1]);
|
|
866
1205
|
}
|
|
1206
|
+
const MAX_IMAGE_REFS = 256;
|
|
1207
|
+
const MAX_TOTAL_IMAGE_BYTES = 128 * 1024 * 1024;
|
|
867
1208
|
const images = /* @__PURE__ */ new Map();
|
|
868
1209
|
const refs = /* @__PURE__ */ new Set();
|
|
869
1210
|
for (const m of secXml.matchAll(/binaryItemIDRef="([^"]+)"/g)) refs.add(m[1]);
|
|
1211
|
+
let totalImgBytes = 0;
|
|
870
1212
|
for (const ref of refs) {
|
|
1213
|
+
if (images.size >= MAX_IMAGE_REFS) {
|
|
1214
|
+
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`);
|
|
1215
|
+
break;
|
|
1216
|
+
}
|
|
871
1217
|
let href = binmap.get(ref);
|
|
872
1218
|
if (!href) {
|
|
873
1219
|
const cand = zip.file(new RegExp(`BinData/.*${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i"))[0];
|
|
@@ -881,6 +1227,11 @@ async function renderHwpxToSvg(input, options) {
|
|
|
881
1227
|
warnings.push(`\uC774\uBBF8\uC9C0 ${href} ${(bytes.length / 1048576).toFixed(1)}MB \u2014 \uD55C\uB3C4 \uCD08\uACFC\uB85C \uC0DD\uB7B5`);
|
|
882
1228
|
continue;
|
|
883
1229
|
}
|
|
1230
|
+
if (totalImgBytes + bytes.length > MAX_TOTAL_IMAGE_BYTES) {
|
|
1231
|
+
warnings.push(`\uC774\uBBF8\uC9C0 \uB204\uC801 ${Math.round(MAX_TOTAL_IMAGE_BYTES / 1048576)}MB \uD55C\uB3C4 \uCD08\uACFC \u2014 \uC774\uD6C4 \uC0DD\uB7B5`);
|
|
1232
|
+
break;
|
|
1233
|
+
}
|
|
1234
|
+
totalImgBytes += bytes.length;
|
|
884
1235
|
images.set(ref, { dataUri: `data:${sniffMime(href, bytes)};base64,${Buffer.from(bytes).toString("base64")}` });
|
|
885
1236
|
}
|
|
886
1237
|
const doc = createXmlParser().parseFromString(secXml, "text/xml");
|
|
@@ -888,32 +1239,68 @@ async function renderHwpxToSvg(input, options) {
|
|
|
888
1239
|
if (!root) throw new KordocError("\uC139\uC158 XML \uD30C\uC2F1 \uC2E4\uD328");
|
|
889
1240
|
const pagePr = findFirst(root, "pagePr");
|
|
890
1241
|
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 -
|
|
1242
|
+
const PW = num2(pagePr, "width", 59528), PH = num2(pagePr, "height", 84188);
|
|
1243
|
+
const ML = num2(margin, "left", 8504);
|
|
1244
|
+
const MT = num2(margin, "top", 5668) + num2(margin, "header", 0);
|
|
1245
|
+
const BODY_H = PH - MT - num2(margin, "bottom", 4252) - num2(margin, "footer", 0);
|
|
1246
|
+
const BODY_W = PW - ML - num2(margin, "right", 8504);
|
|
1247
|
+
if (!hasCache) reflowSection(root, styles, { BODY_W, BODY_H }, options?.reflowMode ?? "keep");
|
|
1248
|
+
const colPr = findFirst(root, "colPr");
|
|
1249
|
+
const multiCol = num2(colPr, "colCount", 1) > 1;
|
|
1250
|
+
const paraSegPages = /* @__PURE__ */ new Map();
|
|
1251
|
+
let nPages = 1;
|
|
1252
|
+
let maxTopV = 0;
|
|
1253
|
+
{
|
|
1254
|
+
let prevV = Number.NEGATIVE_INFINITY;
|
|
1255
|
+
let prevH = Number.NEGATIVE_INFINITY;
|
|
1256
|
+
let cur = 0;
|
|
1257
|
+
for (const p of elements2(root)) {
|
|
1258
|
+
if (ln2(p) !== "p") continue;
|
|
1259
|
+
const lsa = findChildByLocalName(p, "linesegarray");
|
|
1260
|
+
const segEls = lsa ? elements2(lsa).filter((s) => ln2(s) === "lineseg") : [];
|
|
1261
|
+
const pagesOf = [];
|
|
1262
|
+
for (const s of segEls) {
|
|
1263
|
+
const v = num2(s, "vertpos");
|
|
1264
|
+
const h = num2(s, "horzpos");
|
|
1265
|
+
if (v < prevV && (!multiCol || h <= prevH)) cur++;
|
|
1266
|
+
pagesOf.push(cur);
|
|
1267
|
+
maxTopV = Math.max(maxTopV, v + num2(s, "textheight", 1e3));
|
|
1268
|
+
prevV = v;
|
|
1269
|
+
prevH = h;
|
|
1270
|
+
}
|
|
1271
|
+
paraSegPages.set(p, pagesOf);
|
|
1272
|
+
nPages = Math.max(nPages, cur + 1);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
896
1275
|
const ctx = {
|
|
897
|
-
|
|
1276
|
+
pages: Array.from({ length: nPages }, () => []),
|
|
1277
|
+
page: 0,
|
|
898
1278
|
geom: { PW, PH, ML, MT, BODY_W, BODY_H },
|
|
899
1279
|
styles,
|
|
900
1280
|
images,
|
|
1281
|
+
defs: [],
|
|
1282
|
+
highlights: (options?.highlights ?? []).map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0),
|
|
901
1283
|
warnings,
|
|
902
1284
|
warned: /* @__PURE__ */ new Set(),
|
|
903
1285
|
stats: { texts: 0, images: 0, tables: 0 }
|
|
904
1286
|
};
|
|
905
|
-
for (const p of
|
|
906
|
-
if (
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
${
|
|
1287
|
+
for (const p of elements2(root)) {
|
|
1288
|
+
if (ln2(p) !== "p") continue;
|
|
1289
|
+
drawPara(p, ML, MT, BODY_W, ctx, 0, paraSegPages.get(p));
|
|
1290
|
+
}
|
|
1291
|
+
const pageH = nPages === 1 ? Math.max(PH, MT + maxTopV + 2e3) : PH;
|
|
1292
|
+
const GAP = 2400;
|
|
1293
|
+
const totalH = nPages * pageH + (nPages - 1) * GAP;
|
|
1294
|
+
const pagesSvg = ctx.pages.map(
|
|
1295
|
+
(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)">
|
|
1296
|
+
${buf.join("\n")}
|
|
1297
|
+
</g></g>`
|
|
1298
|
+
).join("\n");
|
|
1299
|
+
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">
|
|
1300
|
+
<defs><clipPath id="pgclip"><rect x="0" y="0" width="${pt(PW)}" height="${pt(pageH)}"/></clipPath>${ctx.defs.join("")}</defs>
|
|
1301
|
+
${pagesSvg}
|
|
915
1302
|
</svg>`;
|
|
916
|
-
return { svg, width: Math.round(PW) / 100, height: Math.round(
|
|
1303
|
+
return { svg, width: Math.round(PW) / 100, height: Math.round(totalH) / 100, pageCount: nPages, warnings, stats: ctx.stats };
|
|
917
1304
|
}
|
|
918
1305
|
|
|
919
1306
|
export {
|
|
@@ -935,4 +1322,4 @@ export {
|
|
|
935
1322
|
fitRatioForFewerLines,
|
|
936
1323
|
renderHwpxToSvg
|
|
937
1324
|
};
|
|
938
|
-
//# sourceMappingURL=chunk-
|
|
1325
|
+
//# sourceMappingURL=chunk-734GJLXH.js.map
|