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.
Files changed (48) hide show
  1. package/README.md +21 -1
  2. package/dist/{-OPZIF3QT.js → -EYPKRLMN.js} +13 -7
  3. package/dist/{chunk-ILR7GF3W.js → chunk-734GJLXH.js} +522 -135
  4. package/dist/chunk-734GJLXH.js.map +1 -0
  5. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  6. package/dist/{chunk-2C5GCXPJ.cjs → chunk-DMBTOJK4.cjs} +5 -5
  7. package/dist/chunk-DMBTOJK4.cjs.map +1 -0
  8. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  9. package/dist/{chunk-RUYSNNYZ.js → chunk-Q3IHI3E6.js} +325 -29
  10. package/dist/chunk-Q3IHI3E6.js.map +1 -0
  11. package/dist/{chunk-BUDM6ULT.js → chunk-Q5ZPWQID.js} +5 -5
  12. package/dist/{chunk-BUDM6ULT.js.map → chunk-Q5ZPWQID.js.map} +1 -1
  13. package/dist/{chunk-7Z43WEML.js → chunk-SCFFABCT.js} +2 -2
  14. package/dist/{chunk-NGKIAZ6G.js → chunk-ZRNYYEE6.js} +5 -5
  15. package/dist/{chunk-NGKIAZ6G.js.map → chunk-ZRNYYEE6.js.map} +1 -1
  16. package/dist/cli.js +71 -10
  17. package/dist/cli.js.map +1 -1
  18. package/dist/formula-5NKVS2LR.cjs.map +1 -1
  19. package/dist/index.cjs +1018 -335
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +80 -6
  22. package/dist/index.d.ts +80 -6
  23. package/dist/index.js +897 -214
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp.js +33 -10
  26. package/dist/mcp.js.map +1 -1
  27. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  28. package/dist/{parser-5FSEFR3R.js → parser-76JSTEP3.js} +107 -7
  29. package/dist/parser-76JSTEP3.js.map +1 -0
  30. package/dist/{parser-PCTX7LGN.cjs → parser-O2SD4DFR.cjs} +118 -18
  31. package/dist/parser-O2SD4DFR.cjs.map +1 -0
  32. package/dist/{parser-AWEGMHHV.js → parser-SNBQZO5E.js} +106 -6
  33. package/dist/parser-SNBQZO5E.js.map +1 -0
  34. package/dist/provider-G4C2V2PD.cjs.map +1 -1
  35. package/dist/render-LDI3LO2P.js +9 -0
  36. package/dist/{watch-Y7BAYXJE.js → watch-LEK5U5P6.js} +5 -5
  37. package/package.json +4 -3
  38. package/dist/chunk-2C5GCXPJ.cjs.map +0 -1
  39. package/dist/chunk-ILR7GF3W.js.map +0 -1
  40. package/dist/chunk-RUYSNNYZ.js.map +0 -1
  41. package/dist/parser-5FSEFR3R.js.map +0 -1
  42. package/dist/parser-AWEGMHHV.js.map +0 -1
  43. package/dist/parser-PCTX7LGN.cjs.map +0 -1
  44. package/dist/render-ATQNKOT2.js +0 -9
  45. /package/dist/{-OPZIF3QT.js.map → -EYPKRLMN.js.map} +0 -0
  46. /package/dist/{chunk-7Z43WEML.js.map → chunk-SCFFABCT.js.map} +0 -0
  47. /package/dist/{render-ATQNKOT2.js.map → render-LDI3LO2P.js.map} +0 -0
  48. /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-7Z43WEML.js";
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/svg-render.ts
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 num(el, attr, fallback = 0) {
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 elements(el)) {
461
- if (ln(ch) === name) return ch;
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
- function textOfT(t) {
478
- let s = "";
479
- const walk = (n, d) => {
480
- if (d > 32) return;
481
- const kids = n.childNodes;
482
- if (!kids) return;
483
- for (let i = 0; i < kids.length; i++) {
484
- const c = kids[i];
485
- if (c.nodeType === 3) s += c.textContent ?? "";
486
- else if (c.nodeType === 1) walk(c, d + 1);
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 elements(p)) {
497
- const tag = ln(runEl);
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 elements(runEl)) {
501
- const cn = ln(ch);
712
+ for (const ch of elements2(runEl)) {
713
+ const cn = ln2(ch);
502
714
  if (cn === "t") {
503
- for (const c of textOfT(ch)) chars.push({ ch: c, prId });
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: num(sz, "width"),
513
- height: num(sz, "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 = elements(runEl).filter((s) => ln(s) === "lineseg").map((s) => ({
519
- textpos: num(s, "textpos"),
520
- vertpos: num(s, "vertpos"),
521
- horzpos: num(s, "horzpos"),
522
- horzsize: num(s, "horzsize"),
523
- textheight: num(s, "textheight", 1e3),
524
- baseline: num(s, "baseline", 850)
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 (const plan of plans) {
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 w = measureTextWidth(piece, st.height, st.ratio, { spacingPct: st.spacing }) * plan.scale;
617
- if (piece.trim().length > 0) {
618
- const attrs = [`x="${pt(cursor)}"`, `y="${pt(y)}"`, `font-size="${pt(st.height)}"`];
619
- if ([...piece].length > 1 && w > 50) {
620
- attrs.push(`textLength="${pt(w)}"`, `lengthAdjust="${plan.scale < 1 ? "spacingAndGlyphs" : "spacing"}"`);
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
- 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++;
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 plan = plans[0];
637
- for (const pl of plans) if (pl.start <= o.index && (o.index < pl.end || pl === plans[plans.length - 1])) plan = pl;
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 = num(om, "top"), omB = num(om, "bottom");
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 = num(pos, "vertOffset");
655
- const ho = num(pos, "horzOffset");
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 elements(o.el)) {
688
- const tag = ln(ch);
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: num(sz, "width"), height: num(sz, "height") };
693
- drawObject(sub, x + num(off, "x"), y + num(off, "y"), baseV, areaW, ctx, depth + 1);
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}`, `\uB3C4\uD615 \uAC1C\uCCB4(${o.tag}) \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5`);
959
+ warnOnce(ctx, `shape:${o.tag}`, `\uAC1C\uCCB4(${o.tag}) \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5`);
699
960
  }
700
961
  }
701
- function cellContentExtent(cell, ctx) {
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 elements(cell.sub)) {
705
- if (ln(p) !== "p") continue;
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 - (num(om, "top") + o.height + num(om, "bottom"));
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 + num(om, "top") + num(pos, "vertOffset") + o.height);
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 drawTable(tbl, tx, ty, ctx, depth) {
726
- if (depth > 16) {
727
- warnOnce(ctx, "depth", "\uC911\uCCA9 \uAE4A\uC774 16 \uCD08\uACFC \u2014 \uC774\uD558 \uC0DD\uB7B5");
728
- return;
729
- }
730
- ctx.stats.tables++;
731
- const tblSz = findChildByLocalName(tbl, "sz");
1033
+ function collectCells(tbl) {
732
1034
  const inMargin = findChildByLocalName(tbl, "inMargin");
733
- const defL = num(inMargin, "left", 141), defR = num(inMargin, "right", 141);
734
- const defT = num(inMargin, "top", 141), defB = num(inMargin, "bottom", 141);
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 elements(tbl)) {
737
- if (ln(tr) !== "tr") continue;
738
- for (const tc of elements(tr)) {
739
- if (ln(tc) !== "tc") continue;
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: num(addr, "colAddr"),
748
- ra: num(addr, "rowAddr"),
749
- cs: Math.max(1, num(span, "colSpan", 1)),
750
- rs: Math.max(1, num(span, "rowSpan", 1)),
751
- w: num(csz, "width"),
752
- h: num(csz, "height"),
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 ? num(cm, "left", defL) : defL,
756
- marginR: cm ? num(cm, "right", defR) : defR,
757
- marginT: cm ? num(cm, "top", defT) : defT,
758
- marginB: cm ? num(cm, "bottom", defB) : defB
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, num(tblSz, "width") || void 0);
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, ctx) : void 0 })),
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.svg.push(`<rect x="${pt(g.x)}" y="${pt(g.y)}" width="${pt(g.w)}" height="${pt(g.h)}" fill="${escapeXml(bf.fill)}"/>`);
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, ctx);
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 elements(c.sub)) {
794
- if (ln(p) !== "p") continue;
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.svg.push(edgeLine(g.x, g.y, g.x + g.w, g.y, bf.top));
802
- if (bf.bottom) ctx.svg.push(edgeLine(g.x, g.y + g.h, g.x + g.w, g.y + g.h, bf.bottom));
803
- if (bf.left) ctx.svg.push(edgeLine(g.x, g.y, g.x, g.y + g.h, bf.left));
804
- if (bf.right) ctx.svg.push(edgeLine(g.x + g.w, g.y, g.x + g.w, g.y + g.h, bf.right));
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 = num(sz, "width", 5669), h = num(sz, "height", 5669);
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.svg.push(`<rect x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" fill="#eee" stroke="#c00" stroke-width="0.5"/>`);
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 orgW = num(orgSz, "width"), orgH = num(orgSz, "height");
822
- const cl = num(clip, "left"), ct = num(clip, "top");
823
- const cr = num(clip, "right", orgW), cb = num(clip, "bottom", orgH);
824
- const cropped = orgW > 0 && orgH > 0 && clip != null && (cl > 0 || ct > 0 || cr < orgW || cb < orgH) && cr > cl && cb > ct;
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
- ctx.svg.push(
827
- `<svg x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" viewBox="${pt(cl)} ${pt(ct)} ${pt(cr - cl)} ${pt(cb - ct)}" preserveAspectRatio="none"><image x="0" y="0" width="${pt(orgW)}" height="${pt(orgH)}" preserveAspectRatio="none" href="${loaded.dataUri}"/></svg>`
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.svg.push(`<image x="${pt(x)}" y="${pt(y)}" width="${pt(w)}" height="${pt(h)}" preserveAspectRatio="none" href="${loaded.dataUri}"/>`);
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
- if (!secXml.includes("linesegarray")) {
854
- throw new KordocError("\uC870\uD310 \uCE90\uC2DC(linesegarray) \uC5C6\uC74C \u2014 \uD55C\uCEF4\uC5D0\uC11C \uC800\uC7A5\uD55C HWPX\uB9CC \uB80C\uB354 \uAC00\uB2A5");
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 = num(pagePr, "width", 59528), PH = num(pagePr, "height", 84188);
892
- const ML = num(margin, "left", 8504);
893
- const MT = num(margin, "top", 5668) + num(margin, "header", 0);
894
- const BODY_H = PH - MT - num(margin, "bottom", 4252) - num(margin, "footer", 0);
895
- const BODY_W = PW - ML - num(margin, "right", 8504);
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
- svg: [],
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 elements(root)) {
906
- if (ln(p) !== "p") continue;
907
- const segs = findChildByLocalName(p, "linesegarray");
908
- const first = segs ? elements(segs)[0] : null;
909
- if (first && num(first, "vertpos") > BODY_H) continue;
910
- drawPara(p, ML, MT, BODY_W, ctx, 0);
911
- }
912
- const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pt(PW)} ${pt(PH)}" width="${pt(PW)}pt" height="${pt(PH)}pt" font-family="'HCR Batang','\uD568\uCD08\uB86C\uBC14\uD0D5','Hancom Batang',AppleMyungjo,'Noto Serif CJK KR',serif" xml:space="preserve">
913
- <rect width="100%" height="100%" fill="white"/>
914
- ${ctx.svg.join("\n")}
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(PH) / 100, warnings, stats: ctx.stats };
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-ILR7GF3W.js.map
1325
+ //# sourceMappingURL=chunk-734GJLXH.js.map