kordoc 3.16.2 → 3.18.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 (56) hide show
  1. package/README.md +37 -12
  2. package/dist/{-C4FVXHIP.js → -OUCPI6B3.js} +9 -7
  3. package/dist/{chunk-HECUYZZK.js → chunk-4YEZTZAR.js} +79 -3
  4. package/dist/chunk-4YEZTZAR.js.map +1 -0
  5. package/dist/{chunk-72LLOLFX.cjs → chunk-5HDBLRVP.cjs} +45 -9
  6. package/dist/chunk-5HDBLRVP.cjs.map +1 -0
  7. package/dist/{chunk-4WI7QXEG.js → chunk-6Y4KKPC3.js} +2 -2
  8. package/dist/chunk-DCZVOIEO.cjs.map +1 -1
  9. package/dist/{chunk-EJ2ZIYTO.js → chunk-GIXXV52X.js} +39 -3
  10. package/dist/chunk-GIXXV52X.js.map +1 -0
  11. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  12. package/dist/{chunk-EUEKDZ7O.js → chunk-PWHM3KAA.js} +535 -83
  13. package/dist/chunk-PWHM3KAA.js.map +1 -0
  14. package/dist/{chunk-WR5LHRXO.js → chunk-VP34RO24.js} +3 -3
  15. package/dist/{chunk-IS3IK7UT.js → chunk-YWIYGXLI.js} +39 -3
  16. package/dist/chunk-YWIYGXLI.js.map +1 -0
  17. package/dist/{chunk-E4Y73XRH.js → chunk-ZTUQYITH.js} +127 -68
  18. package/dist/chunk-ZTUQYITH.js.map +1 -0
  19. package/dist/cli.js +18 -15
  20. package/dist/cli.js.map +1 -1
  21. package/dist/formula-5NKVS2LR.cjs.map +1 -1
  22. package/dist/index.cjs +1045 -459
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +121 -3
  25. package/dist/index.d.ts +121 -3
  26. package/dist/index.js +784 -198
  27. package/dist/index.js.map +1 -1
  28. package/dist/mcp.js +22 -10
  29. package/dist/mcp.js.map +1 -1
  30. package/dist/page-range-P7SDW6LR.cjs.map +1 -1
  31. package/dist/{parser-GWL72XYS.js → parser-5D6BQYTX.js} +2 -2
  32. package/dist/{parser-CMPNM6ZL.js → parser-NZAE7PBE.js} +3 -3
  33. package/dist/{parser-YVRTKGKO.cjs → parser-S34YR2SH.cjs} +14 -14
  34. package/dist/parser-S34YR2SH.cjs.map +1 -0
  35. package/dist/provider-G4C2V2PD.cjs.map +1 -1
  36. package/dist/render-HRAW7ASR.js +10 -0
  37. package/dist/seal-CK64CXWO.js +10 -0
  38. package/dist/{watch-WLI6K3IR.js → watch-2EFXBRYK.js} +7 -7
  39. package/package.json +1 -1
  40. package/dist/chunk-72LLOLFX.cjs.map +0 -1
  41. package/dist/chunk-E4Y73XRH.js.map +0 -1
  42. package/dist/chunk-EJ2ZIYTO.js.map +0 -1
  43. package/dist/chunk-EUEKDZ7O.js.map +0 -1
  44. package/dist/chunk-HECUYZZK.js.map +0 -1
  45. package/dist/chunk-IS3IK7UT.js.map +0 -1
  46. package/dist/parser-YVRTKGKO.cjs.map +0 -1
  47. package/dist/render-A2LFMJCP.js +0 -10
  48. package/dist/seal-KA4VAV2Z.js +0 -10
  49. /package/dist/{-C4FVXHIP.js.map → -OUCPI6B3.js.map} +0 -0
  50. /package/dist/{chunk-4WI7QXEG.js.map → chunk-6Y4KKPC3.js.map} +0 -0
  51. /package/dist/{chunk-WR5LHRXO.js.map → chunk-VP34RO24.js.map} +0 -0
  52. /package/dist/{parser-GWL72XYS.js.map → parser-5D6BQYTX.js.map} +0 -0
  53. /package/dist/{parser-CMPNM6ZL.js.map → parser-NZAE7PBE.js.map} +0 -0
  54. /package/dist/{render-A2LFMJCP.js.map → render-HRAW7ASR.js.map} +0 -0
  55. /package/dist/{seal-KA4VAV2Z.js.map → seal-CK64CXWO.js.map} +0 -0
  56. /package/dist/{watch-WLI6K3IR.js.map → watch-2EFXBRYK.js.map} +0 -0
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  classifyError,
12
12
  compareSectionPaths,
13
13
  convertTableToText,
14
+ dedupeRunningHeaders,
14
15
  flattenLayoutTables,
15
16
  isPathTraversal,
16
17
  mapPuaText,
@@ -19,7 +20,7 @@ import {
19
20
  sanitizeHref,
20
21
  stripDtd,
21
22
  toArrayBuffer
22
- } from "./chunk-EJ2ZIYTO.js";
23
+ } from "./chunk-GIXXV52X.js";
23
24
  import {
24
25
  parsePageRange
25
26
  } from "./chunk-GE43BE46.js";
@@ -2527,11 +2528,11 @@ function formatRoman(n, upper) {
2527
2528
  const values = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
2528
2529
  const symbols = upper ? ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"] : ["m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i"];
2529
2530
  let result = "";
2530
- let num3 = n;
2531
+ let num4 = n;
2531
2532
  for (let i = 0; i < values.length; i++) {
2532
- while (num3 >= values[i]) {
2533
+ while (num4 >= values[i]) {
2533
2534
  result += symbols[i];
2534
- num3 -= values[i];
2535
+ num4 -= values[i];
2535
2536
  }
2536
2537
  }
2537
2538
  return result;
@@ -2539,11 +2540,11 @@ function formatRoman(n, upper) {
2539
2540
  function formatLatin(n, upper) {
2540
2541
  if (n <= 0) return "";
2541
2542
  let result = "";
2542
- let num3 = n;
2543
- while (num3 > 0) {
2544
- num3--;
2545
- result = String.fromCharCode((upper ? 65 : 97) + num3 % 26) + result;
2546
- num3 = Math.floor(num3 / 26);
2543
+ let num4 = n;
2544
+ while (num4 > 0) {
2545
+ num4--;
2546
+ result = String.fromCharCode((upper ? 65 : 97) + num4 % 26) + result;
2547
+ num4 = Math.floor(num4 / 26);
2547
2548
  }
2548
2549
  return result;
2549
2550
  }
@@ -2551,15 +2552,15 @@ function formatEastAsianNumber(n, digits, units, zero) {
2551
2552
  if (n === 0) return zero;
2552
2553
  if (n < 0 || n > 99999) return String(n);
2553
2554
  let result = "";
2554
- let num3 = n;
2555
+ let num4 = n;
2555
2556
  let unit = 0;
2556
- while (num3 > 0) {
2557
- const d = num3 % 10;
2557
+ while (num4 > 0) {
2558
+ const d = num4 % 10;
2558
2559
  if (d > 0) {
2559
2560
  const digitStr = d === 1 && unit > 0 ? "" : digits[d];
2560
2561
  result = digitStr + units[unit] + result;
2561
2562
  }
2562
- num3 = Math.floor(num3 / 10);
2563
+ num4 = Math.floor(num4 / 10);
2563
2564
  unit++;
2564
2565
  }
2565
2566
  return result;
@@ -2606,8 +2607,8 @@ function expandNumberingFormat(formatStr, counters, numbering) {
2606
2607
  const idx = levelRef - 1;
2607
2608
  const counterVal = counters[idx] ?? 0;
2608
2609
  const start = numbering.startNumbers[idx] ?? 1;
2609
- const num3 = counterVal > 0 ? start - 1 + counterVal : start;
2610
- result += formatNumber(num3, headFormatToNumFmt(numbering.numberFormats[idx] ?? 0));
2610
+ const num4 = counterVal > 0 ? start - 1 + counterVal : start;
2611
+ result += formatNumber(num4, headFormatToNumFmt(numbering.numberFormats[idx] ?? 0));
2611
2612
  i += 2;
2612
2613
  continue;
2613
2614
  }
@@ -2756,6 +2757,141 @@ function extractHwp5ImagesLenient(lcfb, blocks, warnings) {
2756
2757
  return resolveImageBlocks(binDataMap, blocks, warnings);
2757
2758
  }
2758
2759
 
2760
+ // src/image/transcode.ts
2761
+ import { deflateSync } from "zlib";
2762
+ var CRC_TABLE = (() => {
2763
+ const table = new Uint32Array(256);
2764
+ for (let n = 0; n < 256; n++) {
2765
+ let c = n;
2766
+ for (let k = 0; k < 8; k++) {
2767
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
2768
+ }
2769
+ table[n] = c >>> 0;
2770
+ }
2771
+ return table;
2772
+ })();
2773
+ function crc32(bytes) {
2774
+ let c = 4294967295;
2775
+ for (let i = 0; i < bytes.length; i++) {
2776
+ c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
2777
+ }
2778
+ return (c ^ 4294967295) >>> 0;
2779
+ }
2780
+ var BI_RGB = 0;
2781
+ var MAX_DIM = 32767;
2782
+ var MAX_PIXELS = 64e6;
2783
+ function bmpToPng(bmp) {
2784
+ if (bmp.length < 54) return null;
2785
+ if (bmp[0] !== 66 || bmp[1] !== 77) return null;
2786
+ const dv = new DataView(bmp.buffer, bmp.byteOffset, bmp.byteLength);
2787
+ const dataOffset = dv.getUint32(10, true);
2788
+ const headerSize = dv.getUint32(14, true);
2789
+ if (headerSize < 40) return null;
2790
+ const width = dv.getInt32(18, true);
2791
+ const rawHeight = dv.getInt32(22, true);
2792
+ const bitCount = dv.getUint16(28, true);
2793
+ const compression = dv.getUint32(30, true);
2794
+ if (compression !== BI_RGB) return null;
2795
+ if (bitCount !== 24 && bitCount !== 32) return null;
2796
+ if (width <= 0 || rawHeight === 0) return null;
2797
+ if (width > MAX_DIM || Math.abs(rawHeight) > MAX_DIM) return null;
2798
+ const topDown = rawHeight < 0;
2799
+ const height = Math.abs(rawHeight);
2800
+ if (width * height > MAX_PIXELS) return null;
2801
+ const bytesPerPixel = bitCount >> 3;
2802
+ const rowStride = width * bytesPerPixel + 3 & ~3;
2803
+ if (dataOffset + rowStride * height > bmp.length) return null;
2804
+ const rgba = new Uint8Array(width * height * 4);
2805
+ let anyAlpha = 0;
2806
+ for (let y = 0; y < height; y++) {
2807
+ const srcRow = topDown ? y : height - 1 - y;
2808
+ let src = dataOffset + srcRow * rowStride;
2809
+ let dst = y * width * 4;
2810
+ for (let x = 0; x < width; x++) {
2811
+ rgba[dst] = bmp[src + 2];
2812
+ rgba[dst + 1] = bmp[src + 1];
2813
+ rgba[dst + 2] = bmp[src];
2814
+ const a = bitCount === 32 ? bmp[src + 3] : 255;
2815
+ rgba[dst + 3] = a;
2816
+ anyAlpha |= a;
2817
+ src += bytesPerPixel;
2818
+ dst += 4;
2819
+ }
2820
+ }
2821
+ if (bitCount === 32 && anyAlpha === 0) {
2822
+ for (let i = 3; i < rgba.length; i += 4) rgba[i] = 255;
2823
+ }
2824
+ return encodePng(width, height, rgba);
2825
+ }
2826
+ var PNG_SIGNATURE = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10);
2827
+ function chunk(type, data) {
2828
+ const body = new Uint8Array(4 + data.length);
2829
+ body[0] = type.charCodeAt(0);
2830
+ body[1] = type.charCodeAt(1);
2831
+ body[2] = type.charCodeAt(2);
2832
+ body[3] = type.charCodeAt(3);
2833
+ body.set(data, 4);
2834
+ const out = new Uint8Array(8 + data.length + 4);
2835
+ const dv = new DataView(out.buffer);
2836
+ dv.setUint32(0, data.length, false);
2837
+ out.set(body, 4);
2838
+ dv.setUint32(8 + data.length, crc32(body), false);
2839
+ return out;
2840
+ }
2841
+ function encodePng(width, height, rgba) {
2842
+ const ihdr = new Uint8Array(13);
2843
+ const iv = new DataView(ihdr.buffer);
2844
+ iv.setUint32(0, width, false);
2845
+ iv.setUint32(4, height, false);
2846
+ ihdr[8] = 8;
2847
+ ihdr[9] = 6;
2848
+ const stride = width * 4;
2849
+ const raw = new Uint8Array((stride + 1) * height);
2850
+ for (let y = 0; y < height; y++) {
2851
+ const rowStart = y * (stride + 1);
2852
+ raw[rowStart] = 0;
2853
+ raw.set(rgba.subarray(y * stride, y * stride + stride), rowStart + 1);
2854
+ }
2855
+ const idat = deflateSync(raw);
2856
+ const ihdrChunk = chunk("IHDR", ihdr);
2857
+ const idatChunk = chunk("IDAT", idat);
2858
+ const iendChunk = chunk("IEND", new Uint8Array(0));
2859
+ const out = new Uint8Array(PNG_SIGNATURE.length + ihdrChunk.length + idatChunk.length + iendChunk.length);
2860
+ let o = 0;
2861
+ out.set(PNG_SIGNATURE, o);
2862
+ o += PNG_SIGNATURE.length;
2863
+ out.set(ihdrChunk, o);
2864
+ o += ihdrChunk.length;
2865
+ out.set(idatChunk, o);
2866
+ o += idatChunk.length;
2867
+ out.set(iendChunk, o);
2868
+ return out;
2869
+ }
2870
+ var MAX_INLINE_MD_BYTES = 4 * 1024 * 1024;
2871
+ function escapeRegExp(s) {
2872
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2873
+ }
2874
+ function inlineImagesIntoMarkdown(markdown, images, opts) {
2875
+ const compress = opts?.compress !== false;
2876
+ let out = markdown;
2877
+ for (const img of images) {
2878
+ let bytes = img.data;
2879
+ let mime = img.mimeType;
2880
+ if (compress && mime === "image/bmp") {
2881
+ const png = bmpToPng(img.data);
2882
+ if (png) {
2883
+ bytes = png;
2884
+ mime = "image/png";
2885
+ }
2886
+ }
2887
+ const base64 = Buffer.from(bytes).toString("base64");
2888
+ const dataUri = `data:${mime};base64,${base64}`;
2889
+ const re = new RegExp(`!\\[image\\]\\((?:images/)?${escapeRegExp(img.filename)}\\)`, "g");
2890
+ out = out.replace(re, () => `![image](${dataUri})`);
2891
+ }
2892
+ return out;
2893
+ }
2894
+
2759
2895
  // src/hwp5/aes.ts
2760
2896
  var S_BOX = new Uint8Array([
2761
2897
  99,
@@ -3804,12 +3940,25 @@ function parseHwp5Document(buffer, options) {
3804
3940
  }
3805
3941
  const blocks = [...doc.headerBlocks, ...bodyBlocks, ...doc.footerBlocks];
3806
3942
  const images = cfb ? extractHwp5Images(cfb.FileIndex, blocks, warnings) : extractHwp5ImagesLenient(lenientCfb, blocks, warnings);
3807
- const flatBlocks = flattenLayoutTables(blocks);
3943
+ let flatBlocks = flattenLayoutTables(blocks);
3944
+ if (options?.dedupeRunningHeaders) {
3945
+ const deduped = dedupeRunningHeaders(flatBlocks);
3946
+ const removed = flatBlocks.length - deduped.length;
3947
+ if (removed > 0) warnings.push({ message: `\uBC18\uBCF5 \uB7EC\uB2DD \uD5E4\uB354 ${removed}\uAC1C \uC81C\uAC70`, code: "HIDDEN_TEXT_FILTERED" });
3948
+ flatBlocks = deduped;
3949
+ }
3808
3950
  if (docInfo) {
3809
3951
  detectHwp5Headings(flatBlocks, docInfo);
3810
3952
  }
3811
3953
  const outline = flatBlocks.filter((b) => b.type === "heading" && b.level && b.text).map((b) => ({ level: b.level, text: b.text, pageNumber: b.pageNumber }));
3812
- const markdown = blocksToMarkdown(flatBlocks);
3954
+ let markdown = blocksToMarkdown(flatBlocks);
3955
+ if (options?.inlineImages && images.length > 0) {
3956
+ try {
3957
+ markdown = inlineImagesIntoMarkdown(markdown, images, { compress: true });
3958
+ } catch (inlineErr) {
3959
+ warnings.push({ message: `\uC774\uBBF8\uC9C0 \uC778\uB77C\uC778 \uC2E4\uD328 \u2014 \uC6D0\uBCF8 \uD30C\uC77C \uCC38\uC870\uB85C \uD3F4\uBC31: ${inlineErr instanceof Error ? inlineErr.message : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958"}`, code: "SKIPPED_IMAGE" });
3960
+ }
3961
+ }
3813
3962
  return { markdown, blocks: flatBlocks, metadata, outline: outline.length > 0 ? outline : void 0, warnings: warnings.length > 0 ? warnings : void 0, images: images.length > 0 ? images : void 0 };
3814
3963
  }
3815
3964
  function parseDocInfoStream(cfb, compressed) {
@@ -4175,11 +4324,11 @@ function applyCtrlEffect(ctrl, records, ctx) {
4175
4324
  const attr = ctrl.data.readUInt32LE(4);
4176
4325
  const type = attr & 15;
4177
4326
  const format = attr >>> 4 & 255;
4178
- const num3 = ctx.doc.autoCounters.get(type) ?? 1;
4179
- ctx.doc.autoCounters.set(type, num3 + 1);
4327
+ const num4 = ctx.doc.autoCounters.get(type) ?? 1;
4328
+ ctx.doc.autoCounters.set(type, num4 + 1);
4180
4329
  const prefix = ctrl.data.length >= 14 ? wcharAt(ctrl.data, 12) : "";
4181
4330
  const suffix = ctrl.data.length >= 16 ? wcharAt(ctrl.data, 14) : "";
4182
- ctrl.inlineText = `${prefix}${formatNumber(num3, shapeFormatToNumFmt(format))}${suffix}`;
4331
+ ctrl.inlineText = `${prefix}${formatNumber(num4, shapeFormatToNumFmt(format))}${suffix}`;
4183
4332
  }
4184
4333
  return;
4185
4334
  }
@@ -4187,8 +4336,8 @@ function applyCtrlEffect(ctrl, records, ctx) {
4187
4336
  if (ctrl.data.length >= 10) {
4188
4337
  const attr = ctrl.data.readUInt32LE(4);
4189
4338
  const type = attr & 15;
4190
- const num3 = ctrl.data.readUInt16LE(8);
4191
- if (num3 > 0) ctx.doc.autoCounters.set(type, num3);
4339
+ const num4 = ctrl.data.readUInt16LE(8);
4340
+ if (num4 > 0) ctx.doc.autoCounters.set(type, num4);
4192
4341
  }
4193
4342
  return;
4194
4343
  }
@@ -4250,7 +4399,7 @@ function blocksPlainText(blocks, sep) {
4250
4399
  return parts.join(sep).trim();
4251
4400
  }
4252
4401
  function applyNoteEffect(ctrl, records, ctx, autoType) {
4253
- const num3 = ctx.doc.autoCounters.get(autoType) ?? 1;
4402
+ const num4 = ctx.doc.autoCounters.get(autoType) ?? 1;
4254
4403
  let before = "";
4255
4404
  let after = "";
4256
4405
  let shape = 0;
@@ -4261,11 +4410,11 @@ function applyNoteEffect(ctrl, records, ctx, autoType) {
4261
4410
  if (ctrl.data.length >= 16) {
4262
4411
  shape = ctrl.data.readUInt32LE(12) & 255;
4263
4412
  }
4264
- const formatted = formatNumber(num3, shapeFormatToNumFmt(shape));
4413
+ const formatted = formatNumber(num4, shapeFormatToNumFmt(shape));
4265
4414
  const marker = before || after ? `${before}${formatted}${after}` : `${formatted})`;
4266
4415
  const content = blocksPlainText(parseListHeaderParagraphs(ctrl, records, ctx), " ");
4267
- if ((ctx.doc.autoCounters.get(autoType) ?? 1) <= num3) {
4268
- ctx.doc.autoCounters.set(autoType, num3 + 1);
4416
+ if ((ctx.doc.autoCounters.get(autoType) ?? 1) <= num4) {
4417
+ ctx.doc.autoCounters.set(autoType, num4 + 1);
4269
4418
  }
4270
4419
  ctrl.inlineText = marker;
4271
4420
  if (content) ctrl.footnote = content.startsWith(marker) ? content : `${marker} ${content}`;
@@ -16803,9 +16952,9 @@ var MAX_ROWS2 = 1e4;
16803
16952
  var MAX_COLS2 = 200;
16804
16953
  function cleanNumericValue(raw) {
16805
16954
  if (!/^-?\d+\.\d+$/.test(raw)) return raw;
16806
- const num3 = parseFloat(raw);
16807
- if (!isFinite(num3)) return raw;
16808
- const cleaned = parseFloat(num3.toPrecision(15)).toString();
16955
+ const num4 = parseFloat(raw);
16956
+ if (!isFinite(num4)) return raw;
16957
+ const cleaned = parseFloat(num4.toPrecision(15)).toString();
16809
16958
  return cleaned;
16810
16959
  }
16811
16960
  function parseCellRef(ref) {
@@ -17148,17 +17297,17 @@ function decodeRk(rk) {
17148
17297
  const fDiv100 = (rk & 1) !== 0;
17149
17298
  const fInt = (rk & 2) !== 0;
17150
17299
  const val30 = rk >> 2;
17151
- let num3;
17300
+ let num4;
17152
17301
  if (fInt) {
17153
- num3 = val30;
17302
+ num4 = val30;
17154
17303
  } else {
17155
17304
  const high32 = (rk & 4294967292) >>> 0;
17156
17305
  const buf = Buffer.alloc(8);
17157
17306
  buf.writeUInt32LE(0, 0);
17158
17307
  buf.writeUInt32LE(high32, 4);
17159
- num3 = buf.readDoubleLE(0);
17308
+ num4 = buf.readDoubleLE(0);
17160
17309
  }
17161
- return fDiv100 ? num3 / 100 : num3;
17310
+ return fDiv100 ? num4 / 100 : num4;
17162
17311
  }
17163
17312
  function decodeMulRk(data) {
17164
17313
  if (data.length < 6) return null;
@@ -17858,9 +18007,9 @@ function nodeToLatex(el) {
17858
18007
  case "f": {
17859
18008
  const n = firstKid(el, "num");
17860
18009
  const d = firstKid(el, "den");
17861
- const num3 = n ? childrenToLatex(n) : "";
18010
+ const num4 = n ? childrenToLatex(n) : "";
17862
18011
  const den = d ? childrenToLatex(d) : "";
17863
- return "\\frac" + grp(num3) + grp(den);
18012
+ return "\\frac" + grp(num4) + grp(den);
17864
18013
  }
17865
18014
  // 첨자
17866
18015
  case "sSup": {
@@ -20155,7 +20304,7 @@ function parseCentralDirectory(buf) {
20155
20304
  }
20156
20305
  return { entries, cdOffset, cdSize, eocdOffset };
20157
20306
  }
20158
- var CRC_TABLE = (() => {
20307
+ var CRC_TABLE2 = (() => {
20159
20308
  const table = new Uint32Array(256);
20160
20309
  for (let n = 0; n < 256; n++) {
20161
20310
  let c = n;
@@ -20164,10 +20313,10 @@ var CRC_TABLE = (() => {
20164
20313
  }
20165
20314
  return table;
20166
20315
  })();
20167
- function crc32(data) {
20316
+ function crc322(data) {
20168
20317
  let crc = 4294967295;
20169
20318
  for (let i = 0; i < data.length; i++) {
20170
- crc = CRC_TABLE[(crc ^ data[i]) & 255] ^ crc >>> 8;
20319
+ crc = CRC_TABLE2[(crc ^ data[i]) & 255] ^ crc >>> 8;
20171
20320
  }
20172
20321
  return (crc ^ 4294967295) >>> 0;
20173
20322
  }
@@ -20206,7 +20355,7 @@ function patchZipEntries(original, replacements, additions) {
20206
20355
  const hview = new DataView(header.buffer, header.byteOffset, header.byteLength);
20207
20356
  const method = e.method;
20208
20357
  const compData = method === 0 ? newData : new Uint8Array(deflateRawSync(newData));
20209
- const crc = crc32(newData);
20358
+ const crc = crc322(newData);
20210
20359
  const flags = e.flags & ~8;
20211
20360
  hview.setUint16(6, flags, true);
20212
20361
  hview.setUint32(14, crc, true);
@@ -20224,7 +20373,7 @@ function patchZipEntries(original, replacements, additions) {
20224
20373
  const deflated = new Uint8Array(deflateRawSync(data));
20225
20374
  const method = deflated.length < data.length ? 8 : 0;
20226
20375
  const compData = method === 8 ? deflated : data;
20227
- const crc = crc32(data);
20376
+ const crc = crc322(data);
20228
20377
  const header = new Uint8Array(30 + nameBytes.length);
20229
20378
  const hv = new DataView(header.buffer);
20230
20379
  hv.setUint32(0, LOCAL_SIG, true);
@@ -21253,7 +21402,7 @@ function generateManifest(chartParts = []) {
21253
21402
  </opf:spine>
21254
21403
  </opf:package>`;
21255
21404
  }
21256
- function buildCharProperties(theme, gongmun, ratioVariants = []) {
21405
+ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
21257
21406
  let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
21258
21407
  if (gongmun) {
21259
21408
  body = gongmun.bodyHeight;
@@ -21285,6 +21434,7 @@ function buildCharProperties(theme, gongmun, ratioVariants = []) {
21285
21434
  charPr(rows.length + 3, body, true, true, 0, theme.body, r)
21286
21435
  );
21287
21436
  }
21437
+ rows.push(...extraCharPrXmls);
21288
21438
  return `<hh:charProperties itemCnt="${rows.length}">
21289
21439
  ${rows.join("\n")}
21290
21440
  </hh:charProperties>`;
@@ -21338,9 +21488,9 @@ ${heads}
21338
21488
  </hh:numbering>
21339
21489
  </hh:numberings>`;
21340
21490
  }
21341
- function generateHeaderXml(theme, gongmun, ratioVariants = []) {
21491
+ function generateHeaderXml(theme, gongmun, ratioVariants = [], remap = null) {
21342
21492
  const bodyFace = gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5";
21343
- const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants);
21493
+ const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants, remap?.charPrXmls ?? []);
21344
21494
  const paraPropsXml = buildParaProperties(gongmun);
21345
21495
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
21346
21496
  <hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
@@ -21395,7 +21545,7 @@ function generateHeaderXml(theme, gongmun, ratioVariants = []) {
21395
21545
  </hh:font>
21396
21546
  </hh:fontface>
21397
21547
  </hh:fontfaces>
21398
- <hh:borderFills itemCnt="2">
21548
+ <hh:borderFills itemCnt="${2 + (remap?.borderFillXmls.length ?? 0)}">
21399
21549
  <hh:borderFill id="1" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
21400
21550
  <hh:slash type="NONE" Crooked="0" isCounter="0"/>
21401
21551
  <hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
@@ -21411,7 +21561,7 @@ function generateHeaderXml(theme, gongmun, ratioVariants = []) {
21411
21561
  <hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/>
21412
21562
  <hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/>
21413
21563
  <hh:bottomBorder type="SOLID" width="0.12 mm" color="#000000"/>
21414
- </hh:borderFill>
21564
+ </hh:borderFill>${remap && remap.borderFillXmls.length ? "\n" + remap.borderFillXmls.join("\n") : ""}
21415
21565
  </hh:borderFills>
21416
21566
  ${charPropsXml}
21417
21567
  <hh:tabProperties itemCnt="0"/>
@@ -22143,23 +22293,47 @@ var tableIdCounter = TABLE_ID_BASE;
22143
22293
  function nextTableId() {
22144
22294
  return ++tableIdCounter;
22145
22295
  }
22146
- function generateTable(rows, theme) {
22296
+ var warnedTables = /* @__PURE__ */ new Set();
22297
+ function warnProfileMismatch(tblId, tp, rowCnt, colCnt) {
22298
+ if (warnedTables.has(tblId)) return;
22299
+ warnedTables.add(tblId);
22300
+ console.warn(`[kordoc] format profile: table ${tp.rows}x${tp.cols} vs document ${rowCnt}x${colCnt} \u2014 \uD504\uB85C\uD544 \uBB34\uC2DC`);
22301
+ }
22302
+ function usableProfile(tp, tblId, rowCnt, colCnt) {
22303
+ if (!tp) return null;
22304
+ if (tp.rows !== rowCnt || tp.cols !== colCnt) {
22305
+ warnProfileMismatch(tblId, tp, rowCnt, colCnt);
22306
+ return null;
22307
+ }
22308
+ return tp;
22309
+ }
22310
+ function resolveColWidths(tp, colCnt, fallbackTotal) {
22311
+ if (tp?.colWidths && tp.colWidths.length === colCnt) return tp.colWidths;
22312
+ const w = tp?.width ? Math.floor(tp.width / colCnt) : Math.floor(fallbackTotal / colCnt);
22313
+ return Array(colCnt).fill(w);
22314
+ }
22315
+ function generateTable(rows, theme, tp = null) {
22147
22316
  const rowCnt = rows.length;
22148
22317
  const colCnt = Math.max(...rows.map((r) => r.length), 1);
22149
- const cellW = Math.floor(44e3 / colCnt);
22150
22318
  const cellH = 1500;
22151
- const tblW = cellW * colCnt;
22152
- const tblH = cellH * rowCnt;
22153
22319
  const tblId = nextTableId();
22320
+ const prof = usableProfile(tp, tblId, rowCnt, colCnt);
22321
+ const colW = resolveColWidths(prof, colCnt, 44e3);
22322
+ const tblW = colW.reduce((a, b) => a + b, 0);
22323
+ const tblH = cellH * rowCnt;
22154
22324
  const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
22155
22325
  const trElements = rows.map((row, rowIdx) => {
22156
22326
  const cells = row.length < colCnt ? [...row, ...Array(colCnt - row.length).fill("")] : row;
22157
22327
  const isHeaderRow = rowIdx === 0;
22158
22328
  const headerCharPr = isHeaderRow && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
22159
22329
  const tdElements = cells.map((cell, colIdx) => {
22160
- const runs = generateRuns(cell, headerCharPr);
22330
+ const k = `${rowIdx},${colIdx}`;
22331
+ const bf = prof?.cellBf.get(k) ?? 2;
22332
+ const ch = prof?.cellChar.get(k) ?? headerCharPr;
22333
+ const h = prof?.cellH.get(k) ?? cellH;
22334
+ const runs = generateRuns(cell, ch);
22161
22335
  const p = `<hp:p paraPrIDRef="0" styleIDRef="0">${runs}</hp:p>`;
22162
- return `<hp:tc name="" header="${isHeaderRow ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="2"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${p}</hp:subList><hp:cellAddr colAddr="${colIdx}" rowAddr="${rowIdx}"/><hp:cellSpan colSpan="1" rowSpan="1"/><hp:cellSz width="${cellW}" height="${cellH}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
22336
+ return `<hp:tc name="" header="${isHeaderRow ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${p}</hp:subList><hp:cellAddr colAddr="${colIdx}" rowAddr="${rowIdx}"/><hp:cellSpan colSpan="1" rowSpan="1"/><hp:cellSz width="${colW[colIdx]}" height="${h}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
22163
22337
  }).join("");
22164
22338
  return `<hp:tr>${tdElements}</hp:tr>`;
22165
22339
  }).join("");
@@ -22190,35 +22364,42 @@ function layoutHtmlRows(rows) {
22190
22364
  function unescapeHtml(s) {
22191
22365
  return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&amp;/g, "&");
22192
22366
  }
22193
- function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3) {
22367
+ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, tp = null) {
22194
22368
  const rows = parseHtmlTable(rawHtml);
22195
22369
  if (!rows || rows.length === 0) return null;
22196
22370
  const { placed, rowCnt, colCnt } = layoutHtmlRows(rows);
22197
22371
  if (rowCnt === 0 || colCnt === 0) return null;
22198
- const colW = Math.floor(totalWidth / colCnt);
22199
22372
  const cellH = 1500;
22200
- const tblW = colW * colCnt;
22201
22373
  const tblId = nextTableId();
22374
+ const prof = usableProfile(tp, tblId, rowCnt, colCnt);
22375
+ const colW = resolveColWidths(prof, colCnt, totalWidth);
22376
+ const tblW = colW.reduce((a, b) => a + b, 0);
22202
22377
  const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
22378
+ const spanW = (c, colSpan) => colW.slice(c, c + colSpan).reduce((a, b) => a + b, 0);
22203
22379
  const tcXmls = placed.map((cell) => {
22380
+ const k = `${cell.r},${cell.c}`;
22381
+ const bf = prof?.cellBf.get(k) ?? 2;
22204
22382
  const headerCharPr = cell.isHeader && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
22383
+ const ch = prof?.cellChar.get(k) ?? headerCharPr;
22205
22384
  const { lines } = htmlCellInnerToLines(cell.inner);
22206
22385
  const paras = lines.map(
22207
- (line) => `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t>${escapeXml(unescapeHtml(line))}</hp:t></hp:run></hp:p>`
22386
+ (line) => `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${ch}"><hp:t>${escapeXml(unescapeHtml(line))}</hp:t></hp:run></hp:p>`
22208
22387
  );
22388
+ const cellW = spanW(cell.c, cell.colSpan);
22209
22389
  let nestedH = 0;
22210
22390
  for (const nested of extractTopLevelTables(cell.inner)) {
22211
- const nestedXml = generateHtmlTableXml(nested, theme, Math.max(colW * cell.colSpan - 1020, 4e3));
22391
+ const nestedXml = generateHtmlTableXml(nested, theme, Math.max(cellW - 1020, 4e3));
22212
22392
  if (nestedXml) {
22213
22393
  paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${nestedXml}</hp:run></hp:p>`);
22214
22394
  nestedH += (nested.match(/<tr[\s>]/gi) ?? []).length * cellH + 300;
22215
22395
  }
22216
22396
  }
22217
22397
  if (paras.length === 0) {
22218
- paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t></hp:t></hp:run></hp:p>`);
22398
+ paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${ch}"><hp:t></hp:t></hp:run></hp:p>`);
22219
22399
  }
22220
- const cellHeight = Math.max(cellH * cell.rowSpan, Math.max(lines.length, 1) * 800 + nestedH);
22221
- return `<hp:tc name="" header="${cell.isHeader ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="2"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${paras.join("")}</hp:subList><hp:cellAddr colAddr="${cell.c}" rowAddr="${cell.r}"/><hp:cellSpan colSpan="${cell.colSpan}" rowSpan="${cell.rowSpan}"/><hp:cellSz width="${colW * cell.colSpan}" height="${cellHeight}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
22400
+ const contentH = Math.max(cellH * cell.rowSpan, Math.max(lines.length, 1) * 800 + nestedH);
22401
+ const cellHeight = Math.max(prof?.cellH.get(k) ?? 0, contentH);
22402
+ return `<hp:tc name="" header="${cell.isHeader ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${bf}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${paras.join("")}</hp:subList><hp:cellAddr colAddr="${cell.c}" rowAddr="${cell.r}"/><hp:cellSpan colSpan="${cell.colSpan}" rowSpan="${cell.rowSpan}"/><hp:cellSz width="${cellW}" height="${cellHeight}"/><hp:cellMargin left="141" right="141" top="141" bottom="141"/></hp:tc>`;
22222
22403
  });
22223
22404
  const trXmls = [];
22224
22405
  for (let r = 0; r < rowCnt; r++) {
@@ -22384,9 +22565,9 @@ function readCommand(input, idx, depth) {
22384
22565
  const command = name.value;
22385
22566
  if (command === "\\") return { value: "#", next: name.next };
22386
22567
  if (command === "frac") {
22387
- const num3 = readGroupOrToken(input, name.next, depth);
22388
- const den = readGroupOrToken(input, num3.next, depth);
22389
- return { value: `{${num3.value}} over {${den.value}}`, next: den.next };
22568
+ const num4 = readGroupOrToken(input, name.next, depth);
22569
+ const den = readGroupOrToken(input, num4.next, depth);
22570
+ return { value: `{${num4.value}} over {${den.value}}`, next: den.next };
22390
22571
  }
22391
22572
  if (command === "sqrt") {
22392
22573
  let cursor = skipSpaces(input, name.next);
@@ -22517,9 +22698,10 @@ function generateSecPr(gongmun) {
22517
22698
  } : { top: 8504, bottom: 4252, left: 5670, right: 4252, header: 2835, footer: 2835 };
22518
22699
  return `<hp:secPr textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" outlineShapeIDRef="1" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:pagePr landscape="WIDELY" width="59528" height="84188" gutterType="LEFT_ONLY"><hp:margin header="${m.header}" footer="${m.footer}" gutter="0" left="${m.left}" right="${m.right}" top="${m.top}" bottom="${m.bottom}"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="283" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr></hp:secPr>`;
22519
22700
  }
22520
- function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null) {
22701
+ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, remap = null) {
22521
22702
  const paraXmls = [];
22522
22703
  let isFirst = true;
22704
+ let tableSeq = 0;
22523
22705
  const orderedCounters = {};
22524
22706
  let prevWasOrdered = false;
22525
22707
  for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
@@ -22627,11 +22809,12 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22627
22809
  paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
22628
22810
  isFirst = false;
22629
22811
  }
22630
- xml = generateTable(block.rows, theme);
22812
+ xml = generateTable(block.rows, theme, remap?.tables.get(tableSeq) ?? null);
22813
+ tableSeq++;
22631
22814
  }
22632
22815
  break;
22633
22816
  case "html_table": {
22634
- const tbl = generateHtmlTableXml(block.text || "", theme);
22817
+ const tbl = generateHtmlTableXml(block.text || "", theme, 44e3, remap?.tables.get(tableSeq) ?? null);
22635
22818
  if (tbl) {
22636
22819
  if (isFirst) {
22637
22820
  const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
@@ -22639,6 +22822,7 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22639
22822
  isFirst = false;
22640
22823
  }
22641
22824
  xml = `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
22825
+ tableSeq++;
22642
22826
  } else {
22643
22827
  const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
22644
22828
  xml = plain ? generateParagraph(plain) : "";
@@ -22665,6 +22849,93 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
22665
22849
  </hs:sec>`;
22666
22850
  }
22667
22851
 
22852
+ // src/hwpx/gen-profile.ts
22853
+ function parseHu(s) {
22854
+ if (s == null) return void 0;
22855
+ const n = parseInt(String(s).trim(), 10);
22856
+ return Number.isFinite(n) ? n : void 0;
22857
+ }
22858
+ function edgeXml(tag, d) {
22859
+ return d ? `<hh:${tag} type="${d.type}" width="${d.width}" color="${d.color}"/>` : `<hh:${tag} type="NONE" width="0.1 mm" color="#000000"/>`;
22860
+ }
22861
+ function borderFillDefToXml(id, def) {
22862
+ const fill = def.fill?.faceColor ? `<hh:fillBrush><hh:winBrush faceColor="${def.fill.faceColor}" hatchColor="#000000" alpha="0"/></hh:fillBrush>` : "";
22863
+ return ` <hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
22864
+ <hh:slash type="NONE" Crooked="0" isCounter="0"/>
22865
+ <hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
22866
+ ${edgeXml("leftBorder", def.leftBorder)}
22867
+ ${edgeXml("rightBorder", def.rightBorder)}
22868
+ ${edgeXml("topBorder", def.topBorder)}
22869
+ ${edgeXml("bottomBorder", def.bottomBorder)}${fill ? `
22870
+ ${fill}` : ""}
22871
+ </hh:borderFill>`;
22872
+ }
22873
+ function profileCharPrXml(id, def) {
22874
+ const height = parseHu(def.height_hwpunit) ?? 1e3;
22875
+ const color = def.textColor ?? "#000000";
22876
+ const font = def.fontRef_hangul != null ? parseInt(def.fontRef_hangul, 10) || 0 : 0;
22877
+ const boldAttr = def.bold ? ` bold="1"` : "";
22878
+ const italicAttr = def.italic ? ` italic="1"` : "";
22879
+ const underline = def.underline ? `
22880
+ <hh:underline type="BOTTOM" shape="SOLID" color="${color}"/>` : "";
22881
+ return ` <hh:charPr id="${id}" height="${height}" textColor="${color}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
22882
+ <hh:fontRef hangul="${font}" latin="${font}" hanja="${font}" japanese="${font}" other="${font}" symbol="${font}" user="${font}"/>
22883
+ <hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
22884
+ <hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
22885
+ <hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
22886
+ <hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>${underline}
22887
+ </hh:charPr>`;
22888
+ }
22889
+ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
22890
+ const remap = { borderFillXmls: [], charPrXmls: [], tables: /* @__PURE__ */ new Map() };
22891
+ let bfNext = borderFillBase;
22892
+ let charNext = charPrBase;
22893
+ for (const t of profile.tables) {
22894
+ const localBf = {};
22895
+ for (const [key, def] of Object.entries(t.used_border_fills ?? {})) {
22896
+ const gid = bfNext++;
22897
+ remap.borderFillXmls.push(borderFillDefToXml(gid, def));
22898
+ localBf[key] = gid;
22899
+ }
22900
+ const localChar = {};
22901
+ for (const [key, def] of Object.entries(t.used_char_prs ?? {})) {
22902
+ const gid = charNext++;
22903
+ remap.charPrXmls.push(profileCharPrXml(gid, def));
22904
+ localChar[key] = gid;
22905
+ }
22906
+ let colWidths;
22907
+ if (t.col_widths_hwpunit && t.col_widths_hwpunit.length === t.cols) {
22908
+ const parsed = t.col_widths_hwpunit.map(parseHu);
22909
+ if (parsed.every((n) => n != null)) colWidths = parsed;
22910
+ }
22911
+ const tr = {
22912
+ rows: t.rows,
22913
+ cols: t.cols,
22914
+ width: parseHu(t.width_hwpunit),
22915
+ colWidths,
22916
+ cellBf: /* @__PURE__ */ new Map(),
22917
+ cellChar: /* @__PURE__ */ new Map(),
22918
+ cellH: /* @__PURE__ */ new Map()
22919
+ };
22920
+ for (const cell of t.cells) {
22921
+ const k = `${cell.row},${cell.col}`;
22922
+ if (cell.borderFillIDRef != null && cell.borderFillIDRef in localBf) {
22923
+ tr.cellBf.set(k, localBf[cell.borderFillIDRef]);
22924
+ }
22925
+ if (cell.charPrIDRef != null && cell.charPrIDRef in localChar) {
22926
+ tr.cellChar.set(k, localChar[cell.charPrIDRef]);
22927
+ }
22928
+ const h = parseHu(cell.height_hwpunit);
22929
+ if (h != null) tr.cellH.set(k, h);
22930
+ }
22931
+ remap.tables.set(t.table_index, tr);
22932
+ }
22933
+ return remap;
22934
+ }
22935
+ function profileCharPrBase(ratioVariantCount) {
22936
+ return CHAR_VARIANT_BASE + ratioVariantCount * 4;
22937
+ }
22938
+
22668
22939
  // src/hwpx/generator.ts
22669
22940
  async function markdownToHwpx(markdown, options) {
22670
22941
  const theme = resolveTheme(options?.theme);
@@ -22672,13 +22943,14 @@ async function markdownToHwpx(markdown, options) {
22672
22943
  const blocks = parseMarkdownToBlocks(markdown);
22673
22944
  const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
22674
22945
  const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
22946
+ const remap = options?.profile ? buildProfileRemap(options.profile, profileCharPrBase(fit?.variants?.length ?? 0)) : null;
22675
22947
  const chartParts = [];
22676
- const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts);
22948
+ const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, remap);
22677
22949
  const zip = new JSZip7();
22678
22950
  zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
22679
22951
  zip.file("META-INF/container.xml", generateContainerXml());
22680
22952
  zip.file("Contents/content.hpf", generateManifest(chartParts));
22681
- zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? []));
22953
+ zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? [], remap));
22682
22954
  zip.file("Contents/section0.xml", sectionXml);
22683
22955
  for (const part of chartParts) zip.file(part.name, part.xml);
22684
22956
  zip.file("Preview/PrvText.txt", buildPrvText(blocks));
@@ -22833,6 +23105,77 @@ var DEFAULT_PARA_GEOM = {
22833
23105
  spaceAfter: 0
22834
23106
  };
22835
23107
  var DEFAULT_CHAR = { height: 1e3, bold: false, italic: false, underline: false, ratio: 100, spacing: 0 };
23108
+ var FONT_ALIASES = {
23109
+ "\uD568\uCD08\uB86C\uBC14\uD0D5": "'HCR Batang','\uD568\uCD08\uB86C\uBC14\uD0D5','\uD55C\uCEF4\uBC14\uD0D5'",
23110
+ "\uD55C\uCEF4\uBC14\uD0D5": "'HCR Batang','\uD568\uCD08\uB86C\uBC14\uD0D5','\uD55C\uCEF4\uBC14\uD0D5'",
23111
+ "\uD568\uCD08\uB86C\uB3CB\uC6C0": "'HCR Dotum','\uD568\uCD08\uB86C\uB3CB\uC6C0','\uD55C\uCEF4\uB3CB\uC6C0'",
23112
+ "\uD55C\uCEF4\uB3CB\uC6C0": "'HCR Dotum','\uD568\uCD08\uB86C\uB3CB\uC6C0','\uD55C\uCEF4\uB3CB\uC6C0'",
23113
+ "\uB9D1\uC740 \uACE0\uB515": "'Malgun Gothic','\uB9D1\uC740 \uACE0\uB515'",
23114
+ "\uB9D1\uC740\uACE0\uB515": "'Malgun Gothic','\uB9D1\uC740 \uACE0\uB515'",
23115
+ "\uAD74\uB9BC": "'Gulim','\uAD74\uB9BC'",
23116
+ "\uAD74\uB9BC\uCCB4": "'GulimChe','\uAD74\uB9BC\uCCB4','Gulim'",
23117
+ "\uB3CB\uC6C0": "'Dotum','\uB3CB\uC6C0'",
23118
+ "\uB3CB\uC6C0\uCCB4": "'DotumChe','\uB3CB\uC6C0\uCCB4','Dotum'",
23119
+ "\uBC14\uD0D5": "'Batang','\uBC14\uD0D5'",
23120
+ "\uBC14\uD0D5\uCCB4": "'BatangChe','\uBC14\uD0D5\uCCB4','Batang'",
23121
+ "\uAD81\uC11C": "'Gungsuh','\uAD81\uC11C'",
23122
+ "\uAD81\uC11C\uCCB4": "'GungsuhChe','\uAD81\uC11C\uCCB4','Gungsuh'",
23123
+ "\uB098\uB214\uACE0\uB515": "'NanumGothic','\uB098\uB214\uACE0\uB515'",
23124
+ "\uB098\uB214\uBA85\uC870": "'NanumMyeongjo','\uB098\uB214\uBA85\uC870'",
23125
+ "\uB9D1\uC740 \uACE0\uB515 Semilight": "'Malgun Gothic Semilight','\uB9D1\uC740 \uACE0\uB515'"
23126
+ };
23127
+ function isSerifFace(face) {
23128
+ return /바탕|명조|Batang|Myeong|Mincho|궁서|Gungsuh|Serif|신명|순명|Song|송/i.test(face);
23129
+ }
23130
+ function cssQuote(name) {
23131
+ return /^[A-Za-z0-9_-]+$/.test(name) ? name : `'${name.replace(/['"\\]/g, "")}'`;
23132
+ }
23133
+ function hwpFaceToCssStack(face) {
23134
+ const trimmed = (face ?? "").trim();
23135
+ if (!trimmed) return "";
23136
+ const generic = isSerifFace(trimmed) ? "'HCR Batang','Batang','Noto Serif KR',serif" : "'Malgun Gothic','HCR Dotum','Noto Sans KR',sans-serif";
23137
+ const head = FONT_ALIASES[trimmed] ?? cssQuote(trimmed);
23138
+ return `${head},${generic}`;
23139
+ }
23140
+ function collectHangulFonts(root) {
23141
+ const map = /* @__PURE__ */ new Map();
23142
+ const findFaces = (el, depth) => {
23143
+ if (depth > 24) return null;
23144
+ for (const ch of Array.from(el.childNodes)) {
23145
+ if (ch.nodeType !== 1) continue;
23146
+ const e = ch;
23147
+ if ((e.tagName || "").replace(/^[^:]+:/, "") === "fontfaces") return e;
23148
+ const f = findFaces(e, depth + 1);
23149
+ if (f) return f;
23150
+ }
23151
+ return null;
23152
+ };
23153
+ const faces = findFaces(root, 0);
23154
+ if (!faces) return map;
23155
+ let group = null;
23156
+ let firstGroup = null;
23157
+ for (const ch of Array.from(faces.childNodes)) {
23158
+ if (ch.nodeType !== 1) continue;
23159
+ const e = ch;
23160
+ if ((e.tagName || "").replace(/^[^:]+:/, "") !== "fontface") continue;
23161
+ if (!firstGroup) firstGroup = e;
23162
+ if ((e.getAttribute("lang") ?? "").toUpperCase() === "HANGUL") {
23163
+ group = e;
23164
+ break;
23165
+ }
23166
+ }
23167
+ group = group ?? firstGroup;
23168
+ if (!group) return map;
23169
+ for (const ch of Array.from(group.childNodes)) {
23170
+ if (ch.nodeType !== 1) continue;
23171
+ const e = ch;
23172
+ if ((e.tagName || "").replace(/^[^:]+:/, "") !== "font") continue;
23173
+ const id = e.getAttribute("id");
23174
+ const face = e.getAttribute("face");
23175
+ if (id != null && face) map.set(id, face);
23176
+ }
23177
+ return map;
23178
+ }
22836
23179
  function borderWidthPt(v) {
22837
23180
  const n = parseFloat(v ?? "");
22838
23181
  if (!Number.isFinite(n)) return 0.34;
@@ -22884,6 +23227,7 @@ function parseRenderStyles(headXml) {
22884
23227
  const doc = createXmlParser().parseFromString(headXml, "text/xml");
22885
23228
  const root = doc.documentElement;
22886
23229
  if (!root) return styles;
23230
+ const hangulFonts = collectHangulFonts(root);
22887
23231
  const walk = (el) => {
22888
23232
  const tag = (el.tagName || "").replace(/^[^:]+:/, "");
22889
23233
  if (tag === "charPr") {
@@ -22893,6 +23237,9 @@ function parseRenderStyles(headXml) {
22893
23237
  const spacingEl = findChildByLocalName(el, "spacing");
22894
23238
  const underlineEl = findChildByLocalName(el, "underline");
22895
23239
  const textColor = el.getAttribute("textColor");
23240
+ const fontRef = findChildByLocalName(el, "fontRef");
23241
+ const fontId = fontRef?.getAttribute("hangul") ?? fontRef?.getAttribute("latin");
23242
+ const face = fontId != null ? hangulFonts.get(fontId) : void 0;
22896
23243
  styles.charPr.set(id, {
22897
23244
  height: Number(el.getAttribute("height")) || 1e3,
22898
23245
  bold: findChildByLocalName(el, "bold") != null,
@@ -22900,7 +23247,8 @@ function parseRenderStyles(headXml) {
22900
23247
  underline: underlineEl != null && (underlineEl.getAttribute("type") ?? "NONE") !== "NONE",
22901
23248
  color: textColor && textColor !== "#000000" && textColor.toLowerCase() !== "none" ? textColor : void 0,
22902
23249
  ratio: Number(ratioEl?.getAttribute("hangul")) || 100,
22903
- spacing: Number(spacingEl?.getAttribute("hangul")) || 0
23250
+ spacing: Number(spacingEl?.getAttribute("hangul")) || 0,
23251
+ fontFamily: face ? hwpFaceToCssStack(face) : void 0
22904
23252
  });
22905
23253
  }
22906
23254
  } else if (tag === "paraPr") {
@@ -23234,8 +23582,186 @@ async function placeSealHwpx(hwpxBuffer, ops) {
23234
23582
  };
23235
23583
  }
23236
23584
 
23237
- // src/roundtrip/patcher.ts
23585
+ // src/hwpx/extract-profile.ts
23238
23586
  import JSZip9 from "jszip";
23587
+ function elemsByLocal(root, name) {
23588
+ const all = root.getElementsByTagName("*");
23589
+ const out = [];
23590
+ for (let i = 0; i < all.length; i++) {
23591
+ const el = all[i];
23592
+ const tag = (el.tagName || el.localName || "").replace(/^[^:]+:/, "");
23593
+ if (tag === name) out.push(el);
23594
+ }
23595
+ return out;
23596
+ }
23597
+ function borderDefOf(el) {
23598
+ if (!el) return void 0;
23599
+ const type = el.getAttribute("type") || "NONE";
23600
+ const width = el.getAttribute("width") || "0.1 mm";
23601
+ const color = el.getAttribute("color") || "#000000";
23602
+ return { type, width, color };
23603
+ }
23604
+ function parseBorderFills(headerDoc) {
23605
+ const map = /* @__PURE__ */ new Map();
23606
+ for (const bf of elemsByLocal(headerDoc, "borderFill")) {
23607
+ const id = bf.getAttribute("id");
23608
+ if (!id) continue;
23609
+ const def = {
23610
+ leftBorder: borderDefOf(findChildByLocalName(bf, "leftBorder")),
23611
+ rightBorder: borderDefOf(findChildByLocalName(bf, "rightBorder")),
23612
+ topBorder: borderDefOf(findChildByLocalName(bf, "topBorder")),
23613
+ bottomBorder: borderDefOf(findChildByLocalName(bf, "bottomBorder"))
23614
+ };
23615
+ const fillBrush = findChildByLocalName(bf, "fillBrush");
23616
+ const winBrush = fillBrush ? findChildByLocalName(fillBrush, "winBrush") : null;
23617
+ const face = winBrush?.getAttribute("faceColor");
23618
+ if (face && face !== "none") def.fill = { faceColor: face };
23619
+ map.set(id, def);
23620
+ }
23621
+ return map;
23622
+ }
23623
+ function parseCharPrs(headerDoc) {
23624
+ const map = /* @__PURE__ */ new Map();
23625
+ for (const cp of elemsByLocal(headerDoc, "charPr")) {
23626
+ const id = cp.getAttribute("id");
23627
+ if (!id) continue;
23628
+ const def = {};
23629
+ const h = cp.getAttribute("height");
23630
+ if (h) def.height_hwpunit = h;
23631
+ const color = cp.getAttribute("textColor");
23632
+ if (color) def.textColor = color;
23633
+ if (cp.getAttribute("bold") === "1") def.bold = true;
23634
+ if (cp.getAttribute("italic") === "1") def.italic = true;
23635
+ if (findChildByLocalName(cp, "underline")) def.underline = true;
23636
+ const fontRef = findChildByLocalName(cp, "fontRef");
23637
+ const hangul = fontRef?.getAttribute("hangul");
23638
+ if (hangul) def.fontRef_hangul = hangul;
23639
+ map.set(id, def);
23640
+ }
23641
+ return map;
23642
+ }
23643
+ function isTopLevelTable(tbl) {
23644
+ let p = tbl.parentNode;
23645
+ while (p) {
23646
+ const tag = (p.tagName || p.localName || "").replace(/^[^:]+:/, "");
23647
+ if (tag === "tbl") return false;
23648
+ p = p.parentNode;
23649
+ }
23650
+ return true;
23651
+ }
23652
+ function num(s) {
23653
+ if (s == null) return void 0;
23654
+ const n = parseInt(s, 10);
23655
+ return Number.isFinite(n) ? n : void 0;
23656
+ }
23657
+ function parseTable3(tbl, tableIndex, borderFills, charPrs) {
23658
+ const rows = num(tbl.getAttribute("rowCnt")) ?? 0;
23659
+ const cols = num(tbl.getAttribute("colCnt")) ?? 0;
23660
+ const sz = findChildByLocalName(tbl, "sz");
23661
+ const width = sz?.getAttribute("width") || void 0;
23662
+ const cells = [];
23663
+ const usedBf = /* @__PURE__ */ new Set();
23664
+ const usedCp = /* @__PURE__ */ new Set();
23665
+ const colWidths = new Array(cols).fill(void 0);
23666
+ for (const tc of elemsByLocal(tbl, "tc")) {
23667
+ if (nearestTable(tc) !== tbl) continue;
23668
+ const addr = findChildByLocalName(tc, "cellAddr");
23669
+ const span = findChildByLocalName(tc, "cellSpan");
23670
+ const csz = findChildByLocalName(tc, "cellSz");
23671
+ const row = num(addr?.getAttribute("rowAddr")) ?? 0;
23672
+ const col = num(addr?.getAttribute("colAddr")) ?? 0;
23673
+ const colSpan = num(span?.getAttribute("colSpan")) ?? 1;
23674
+ const rowSpan = num(span?.getAttribute("rowSpan")) ?? 1;
23675
+ const bfId = tc.getAttribute("borderFillIDRef") || void 0;
23676
+ const cpId = firstRunCharPr(tc);
23677
+ const cell = { row, col, rowSpan, colSpan };
23678
+ const w = csz?.getAttribute("width");
23679
+ const hh = csz?.getAttribute("height");
23680
+ if (w) cell.width_hwpunit = w;
23681
+ if (hh) cell.height_hwpunit = hh;
23682
+ if (bfId) {
23683
+ cell.borderFillIDRef = bfId;
23684
+ usedBf.add(bfId);
23685
+ }
23686
+ if (cpId) {
23687
+ cell.charPrIDRef = cpId;
23688
+ usedCp.add(cpId);
23689
+ }
23690
+ cells.push(cell);
23691
+ if (row === 0 && colSpan === 1 && col < cols && w) colWidths[col] = w;
23692
+ }
23693
+ const table = {
23694
+ table_index: tableIndex,
23695
+ rows,
23696
+ cols,
23697
+ cells,
23698
+ used_border_fills: pick(borderFills, usedBf)
23699
+ };
23700
+ if (width) table.width_hwpunit = width;
23701
+ if (colWidths.every((w) => w != null)) table.col_widths_hwpunit = colWidths;
23702
+ const cp = pick(charPrs, usedCp);
23703
+ if (Object.keys(cp).length) table.used_char_prs = cp;
23704
+ return table;
23705
+ }
23706
+ function nearestTable(tc) {
23707
+ let p = tc.parentNode;
23708
+ while (p) {
23709
+ const tag = (p.tagName || p.localName || "").replace(/^[^:]+:/, "");
23710
+ if (tag === "tbl") return p;
23711
+ p = p.parentNode;
23712
+ }
23713
+ return null;
23714
+ }
23715
+ function firstRunCharPr(tc) {
23716
+ for (const run of elemsByLocal(tc, "run")) {
23717
+ if (nearestCell(run) !== tc) continue;
23718
+ const id = run.getAttribute("charPrIDRef");
23719
+ if (id) return id;
23720
+ }
23721
+ return void 0;
23722
+ }
23723
+ function nearestCell(el) {
23724
+ let p = el.parentNode;
23725
+ while (p) {
23726
+ const tag = (p.tagName || p.localName || "").replace(/^[^:]+:/, "");
23727
+ if (tag === "tc") return p;
23728
+ p = p.parentNode;
23729
+ }
23730
+ return null;
23731
+ }
23732
+ function pick(map, keys) {
23733
+ const out = {};
23734
+ for (const k of keys) {
23735
+ const v = map.get(k);
23736
+ if (v !== void 0) out[k] = v;
23737
+ }
23738
+ return out;
23739
+ }
23740
+ async function hwpxToProfile(input) {
23741
+ const buf = input instanceof ArrayBuffer ? input : toArrayBuffer(input);
23742
+ const zip = await JSZip9.loadAsync(buf);
23743
+ const parser = createXmlParser();
23744
+ const headerFile = zip.file("Contents/header.xml") ?? zip.file(/[Hh]eader\.xml$/)?.[0];
23745
+ let headerXml = "<root/>";
23746
+ if (headerFile) headerXml = await headerFile.async("text");
23747
+ const headerDoc = parser.parseFromString(headerXml, "text/xml");
23748
+ const borderFills = parseBorderFills(headerDoc);
23749
+ const charPrs = parseCharPrs(headerDoc);
23750
+ const sectionFiles = zip.file(/[Ss]ection\d+\.xml$/).sort((a, b) => (num(a.name.match(/(\d+)\.xml$/)?.[1] ?? null) ?? 0) - (num(b.name.match(/(\d+)\.xml$/)?.[1] ?? null) ?? 0));
23751
+ const tables = [];
23752
+ let tableIndex = 0;
23753
+ for (const f of sectionFiles) {
23754
+ const doc = parser.parseFromString(await f.async("text"), "text/xml");
23755
+ for (const tbl of elemsByLocal(doc, "tbl")) {
23756
+ if (!isTopLevelTable(tbl)) continue;
23757
+ tables.push(parseTable3(tbl, tableIndex++, borderFills, charPrs));
23758
+ }
23759
+ }
23760
+ return { schema_version: "0.1.0", tables };
23761
+ }
23762
+
23763
+ // src/roundtrip/patcher.ts
23764
+ import JSZip10 from "jszip";
23239
23765
 
23240
23766
  // src/roundtrip/table-rows.ts
23241
23767
  var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
@@ -23873,7 +24399,7 @@ async function patchHwpx(original, editedMarkdown, options) {
23873
24399
  }
23874
24400
  let zip;
23875
24401
  try {
23876
- zip = await JSZip9.loadAsync(original);
24402
+ zip = await JSZip10.loadAsync(original);
23877
24403
  } catch {
23878
24404
  return { success: false, applied: 0, skipped, error: "ZIP \uB85C\uB4DC \uC2E4\uD328" };
23879
24405
  }
@@ -23964,16 +24490,16 @@ function buildOrigUnits(blocks) {
23964
24490
  for (let i = 0; i < blocks.length; i++) {
23965
24491
  const block = blocks[i];
23966
24492
  let consume = 1;
23967
- let chunk;
24493
+ let chunk2;
23968
24494
  if (block.type === "paragraph" && block.text && /^\[별표\s*\d+/.test(sanitizeText(block.text))) {
23969
24495
  const next = blocks[i + 1];
23970
24496
  if (next?.type === "paragraph" && next.text && /관련\)?$/.test(next.text)) consume = 2;
23971
- chunk = blocksToMarkdown(blocks.slice(i, i + consume));
24497
+ chunk2 = blocksToMarkdown(blocks.slice(i, i + consume));
23972
24498
  } else {
23973
- chunk = blocksToMarkdown([block]);
24499
+ chunk2 = blocksToMarkdown([block]);
23974
24500
  }
23975
- if (chunk) {
23976
- const subUnits = splitMarkdownUnits(chunk);
24501
+ if (chunk2) {
24502
+ const subUnits = splitMarkdownUnits(chunk2);
23977
24503
  const isFragment = consume === 2 || (block.type === "paragraph" || block.type === "heading") && subUnits.length > 1;
23978
24504
  for (let s = 0; s < subUnits.length; s++) {
23979
24505
  const u = { ...subUnits[s], blockIdx: i, fragment: isFragment || void 0 };
@@ -24807,19 +25333,19 @@ function resolveTableMappings(blocks, scanTables) {
24807
25333
  if (scanTables[k].rows === table.rows && scanTables[k].cols === table.cols) cands.push(k);
24808
25334
  }
24809
25335
  if (cands.length === 0) continue;
24810
- let pick = cands[0];
25336
+ let pick2 = cands[0];
24811
25337
  if (cands.length > 1) {
24812
25338
  let best = tableContentScore(table, scanTables[cands[0]]);
24813
25339
  for (let ci = 1; ci < cands.length; ci++) {
24814
25340
  const sc = tableContentScore(table, scanTables[cands[ci]]);
24815
25341
  if (sc > best) {
24816
25342
  best = sc;
24817
- pick = cands[ci];
25343
+ pick2 = cands[ci];
24818
25344
  }
24819
25345
  }
24820
25346
  }
24821
- result.set(i, scanTables[pick]);
24822
- si = pick + 1;
25347
+ result.set(i, scanTables[pick2]);
25348
+ si = pick2 + 1;
24823
25349
  }
24824
25350
  return result;
24825
25351
  }
@@ -25295,7 +25821,7 @@ function stageParaPatch(scan, para, newPlain, skip) {
25295
25821
  }
25296
25822
 
25297
25823
  // src/validate.ts
25298
- import JSZip10 from "jszip";
25824
+ import JSZip11 from "jszip";
25299
25825
  import { DOMParser as DOMParser5 } from "@xmldom/xmldom";
25300
25826
  var REQUIRED_FILES = [
25301
25827
  "mimetype",
@@ -25313,7 +25839,7 @@ async function validateHwpx(buffer) {
25313
25839
  const issues = [];
25314
25840
  let zip;
25315
25841
  try {
25316
- zip = await JSZip10.loadAsync(buffer);
25842
+ zip = await JSZip11.loadAsync(buffer);
25317
25843
  } catch (err) {
25318
25844
  return {
25319
25845
  ok: false,
@@ -25381,10 +25907,10 @@ async function validateHwpx(buffer) {
25381
25907
  }
25382
25908
 
25383
25909
  // src/roundtrip/session.ts
25384
- import JSZip11 from "jszip";
25910
+ import JSZip12 from "jszip";
25385
25911
  async function buildState(bytes) {
25386
25912
  const parsed = await parseHwpxDocument(u8ToArrayBuffer2(bytes));
25387
- const zip = await JSZip11.loadAsync(bytes);
25913
+ const zip = await JSZip12.loadAsync(bytes);
25388
25914
  const sectionPaths = await resolveSectionEntryNames(zip);
25389
25915
  if (sectionPaths.length === 0) {
25390
25916
  throw new Error("HWPX \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
@@ -25879,7 +26405,7 @@ async function blocksToPdf(blocks, options) {
25879
26405
  }
25880
26406
 
25881
26407
  // src/render/svg-render.ts
25882
- import JSZip12 from "jszip";
26408
+ import JSZip13 from "jszip";
25883
26409
 
25884
26410
  // src/render/layout.ts
25885
26411
  function toInt32(v, fallback = 0) {
@@ -25968,7 +26494,7 @@ function elements(el) {
25968
26494
  for (let i = 0; i < kids2.length; i++) if (kids2[i].nodeType === 1) out.push(kids2[i]);
25969
26495
  return out;
25970
26496
  }
25971
- function num(el, attr, fallback = 0) {
26497
+ function num2(el, attr, fallback = 0) {
25972
26498
  return el ? toInt32(el.getAttribute(attr) ?? void 0, fallback) : fallback;
25973
26499
  }
25974
26500
  function shiftParaVert(p, delta) {
@@ -25976,7 +26502,7 @@ function shiftParaVert(p, delta) {
25976
26502
  if (ln(lsa) !== "linesegarray") continue;
25977
26503
  for (const seg of elements(lsa)) {
25978
26504
  if (ln(seg) !== "lineseg") continue;
25979
- seg.setAttribute("vertpos", String(num(seg, "vertpos") + delta));
26505
+ seg.setAttribute("vertpos", String(num2(seg, "vertpos") + delta));
25980
26506
  }
25981
26507
  }
25982
26508
  }
@@ -26031,7 +26557,7 @@ function reflowPara(p, doc, styles, areaW, startV, mode) {
26031
26557
  const lsa = doc.createElement("hp:linesegarray");
26032
26558
  for (let li = 0; li < wrap.starts.length; li++) {
26033
26559
  const startReal = wrap.starts[li];
26034
- const textpos = startReal < realIdx.length ? realIdx[startReal] : m.chars.length;
26560
+ const textpos = startReal < realIdx.length ? realIdx[startReal] : 0;
26035
26561
  const vertpos = startV + li * pitch;
26036
26562
  const isFirst = li === 0;
26037
26563
  const seg = doc.createElement("hp:lineseg");
@@ -26066,9 +26592,9 @@ function reflowTablesIn(p, doc, styles, mode, counter) {
26066
26592
  if (ln(tc) !== "tc") continue;
26067
26593
  const csz = findChildByLocalName(tc, "cellSz");
26068
26594
  const cm = findChildByLocalName(tc, "cellMargin");
26069
- const cellW = num(csz, "width");
26070
- const mL = cm ? num(cm, "left", 141) : 141;
26071
- const mR = cm ? num(cm, "right", 141) : 141;
26595
+ const cellW = num2(csz, "width");
26596
+ const mL = cm ? num2(cm, "left", 141) : 141;
26597
+ const mR = cm ? num2(cm, "right", 141) : 141;
26072
26598
  const areaW = Math.max(500, cellW - mL - mR);
26073
26599
  const sub = findChildByLocalName(tc, "subList");
26074
26600
  if (sub) reflowBlockFlow(sub, doc, styles, areaW, mode, counter, 0);
@@ -26119,7 +26645,7 @@ function elements2(el) {
26119
26645
  }
26120
26646
  return out;
26121
26647
  }
26122
- function num2(el, attr, fallback = 0) {
26648
+ function num3(el, attr, fallback = 0) {
26123
26649
  return el ? toInt32(el.getAttribute(attr) ?? void 0, fallback) : fallback;
26124
26650
  }
26125
26651
  function findFirst(el, name, depth = 0) {
@@ -26187,8 +26713,8 @@ function buildPara(p) {
26187
26713
  } else if (OBJ_TAGS.has(cn)) {
26188
26714
  const sz = findChildByLocalName(ch, "sz");
26189
26715
  const pos = findChildByLocalName(ch, "pos");
26190
- const w = num2(sz, "width") || num2(findChildByLocalName(ch, "curSz"), "width") || num2(findChildByLocalName(ch, "orgSz"), "width");
26191
- const h = num2(sz, "height") || num2(findChildByLocalName(ch, "curSz"), "height") || num2(findChildByLocalName(ch, "orgSz"), "height");
26716
+ const w = num3(sz, "width") || num3(findChildByLocalName(ch, "curSz"), "width") || num3(findChildByLocalName(ch, "orgSz"), "width");
26717
+ const h = num3(sz, "height") || num3(findChildByLocalName(ch, "curSz"), "height") || num3(findChildByLocalName(ch, "orgSz"), "height");
26192
26718
  objs.push({
26193
26719
  el: ch,
26194
26720
  tag: cn,
@@ -26204,12 +26730,12 @@ function buildPara(p) {
26204
26730
  }
26205
26731
  } else if (tag === "linesegarray") {
26206
26732
  segs = elements2(runEl).filter((s) => ln2(s) === "lineseg").map((s) => ({
26207
- textpos: num2(s, "textpos"),
26208
- vertpos: num2(s, "vertpos"),
26209
- horzpos: num2(s, "horzpos"),
26210
- horzsize: num2(s, "horzsize"),
26211
- textheight: num2(s, "textheight", 1e3),
26212
- baseline: num2(s, "baseline", 850)
26733
+ textpos: num3(s, "textpos"),
26734
+ vertpos: num3(s, "vertpos"),
26735
+ horzpos: num3(s, "horzpos"),
26736
+ horzsize: num3(s, "horzsize"),
26737
+ textheight: num3(s, "textheight", 1e3),
26738
+ baseline: num3(s, "baseline", 850)
26213
26739
  }));
26214
26740
  }
26215
26741
  }
@@ -26311,6 +26837,7 @@ function drawPara(p, ox, oy, areaW, ctx, depth, segPages) {
26311
26837
  }
26312
26838
  if (text.trim().length > 0) {
26313
26839
  const attrs = [`x="${pt(cx)}"`, `y="${pt(y)}"`, `font-size="${pt(st.height)}"`];
26840
+ if (st.fontFamily) attrs.push(`font-family="${st.fontFamily}"`);
26314
26841
  if ([...text].length > 1 && sw > 50) {
26315
26842
  attrs.push(`textLength="${pt(sw)}"`, `lengthAdjust="${plan.scale < 1 ? "spacingAndGlyphs" : "spacing"}"`);
26316
26843
  }
@@ -26378,11 +26905,11 @@ function anchorObject(o, ox, oy, baseV, areaW, ctx) {
26378
26905
  const { PW, PH, ML, MT, BODY_W, BODY_H } = ctx.geom;
26379
26906
  const pos = findChildByLocalName(o.el, "pos");
26380
26907
  const om = findChildByLocalName(o.el, "outMargin");
26381
- const omT = num2(om, "top"), omB = num2(om, "bottom");
26908
+ const omT = num3(om, "top"), omB = num3(om, "bottom");
26382
26909
  const w = o.width, h = o.height;
26383
26910
  if (!pos) return { x: ox, y: oy + baseV };
26384
- const vo = num2(pos, "vertOffset");
26385
- const ho = num2(pos, "horzOffset");
26911
+ const vo = num3(pos, "vertOffset");
26912
+ const ho = num3(pos, "horzOffset");
26386
26913
  const vrel = pos.getAttribute("vertRelTo") ?? "PARA";
26387
26914
  const hrel = pos.getAttribute("horzRelTo") ?? "PARA";
26388
26915
  const va = pos.getAttribute("vertAlign") ?? "TOP";
@@ -26419,8 +26946,8 @@ function drawObject(o, x, y, baseV, areaW, ctx, depth) {
26419
26946
  if (!OBJ_TAGS.has(tag)) continue;
26420
26947
  const sz = findChildByLocalName(ch, "sz");
26421
26948
  const off = findChildByLocalName(ch, "offset");
26422
- const sub = { el: ch, tag, index: 0, inline: true, width: num2(sz, "width"), height: num2(sz, "height") };
26423
- drawObject(sub, x + num2(off, "x"), y + num2(off, "y"), baseV, areaW, ctx, depth + 1);
26949
+ const sub = { el: ch, tag, index: 0, inline: true, width: num3(sz, "width"), height: num3(sz, "height") };
26950
+ drawObject(sub, x + num3(off, "x"), y + num3(off, "y"), baseV, areaW, ctx, depth + 1);
26424
26951
  }
26425
26952
  } else if (o.tag === "equation") {
26426
26953
  warnOnce(ctx, "equation", "\uC218\uC2DD \uAC1C\uCCB4\uB294 \uB80C\uB354 \uBBF8\uC9C0\uC6D0 \u2014 \uC0DD\uB7B5");
@@ -26438,16 +26965,16 @@ function drawShape(o, x, y, ctx, depth) {
26438
26965
  const el = o.el;
26439
26966
  const orgSz = findChildByLocalName(el, "orgSz");
26440
26967
  const curSz = findChildByLocalName(el, "curSz");
26441
- const ow = num2(orgSz, "width"), oh = num2(orgSz, "height");
26442
- const w = num2(curSz, "width") || ow || o.width;
26443
- const h = num2(curSz, "height") || oh || o.height;
26968
+ const ow = num3(orgSz, "width"), oh = num3(orgSz, "height");
26969
+ const w = num3(curSz, "width") || ow || o.width;
26970
+ const h = num3(curSz, "height") || oh || o.height;
26444
26971
  const sx = ow > 0 ? w / ow : 1;
26445
26972
  const sy = oh > 0 ? h / oh : 1;
26446
26973
  const lineShape = findChildByLocalName(el, "lineShape");
26447
26974
  const lstyle = lineShape?.getAttribute("style") ?? "SOLID";
26448
26975
  const strokeCol = lineShape?.getAttribute("color") || "#000000";
26449
26976
  const hasStroke = lstyle !== "NONE";
26450
- const strokeW = hasStroke ? shapeStrokePt(lineShape ? num2(lineShape, "width") : 33) : 0;
26977
+ const strokeW = hasStroke ? shapeStrokePt(lineShape ? num3(lineShape, "width") : 33) : 0;
26451
26978
  const dash = /DASH|DOT/.test(lstyle) ? ` stroke-dasharray="${lstyle.includes("DOT") ? "1,1.5" : "3,1.5"}"` : "";
26452
26979
  const strokeAttr = hasStroke ? ` stroke="${escapeXml3(strokeCol)}" stroke-width="${strokeW.toFixed(2)}"${dash}` : "";
26453
26980
  const fillBrush = findChildByLocalName(el, "fillBrush");
@@ -26461,12 +26988,12 @@ function drawShape(o, x, y, ctx, depth) {
26461
26988
  emit(ctx, `<ellipse cx="${pt(x + w / 2)}" cy="${pt(y + h / 2)}" rx="${pt(w / 2)}" ry="${pt(h / 2)}"${fillAttr}${strokeAttr}/>`);
26462
26989
  } else if (o.tag === "line") {
26463
26990
  const s = findChildByLocalName(el, "startPt"), e = findChildByLocalName(el, "endPt");
26464
- const x1 = x + num2(s, "x") * sx, y1 = y + num2(s, "y") * sy;
26465
- const x2 = x + num2(e, "x") * sx, y2 = y + num2(e, "y") * sy;
26991
+ const x1 = x + num3(s, "x") * sx, y1 = y + num3(s, "y") * sy;
26992
+ const x2 = x + num3(e, "x") * sx, y2 = y + num3(e, "y") * sy;
26466
26993
  emit(ctx, `<line x1="${pt(x1)}" y1="${pt(y1)}" x2="${pt(x2)}" y2="${pt(y2)}" stroke="${escapeXml3(strokeCol)}" stroke-width="${(strokeW || 0.3).toFixed(2)}"${dash}/>`);
26467
26994
  } else if (o.tag === "polygon" || o.tag === "curv") {
26468
26995
  const pts = [];
26469
- for (const c of elements2(el)) if (ln2(c) === "pt") pts.push(`${pt(x + num2(c, "x") * sx)},${pt(y + num2(c, "y") * sy)}`);
26996
+ for (const c of elements2(el)) if (ln2(c) === "pt") pts.push(`${pt(x + num3(c, "x") * sx)},${pt(y + num3(c, "y") * sy)}`);
26470
26997
  if (pts.length >= 2) emit(ctx, `<polygon points="${pts.join(" ")}"${fillAttr}${strokeAttr}/>`);
26471
26998
  } else if (o.tag === "arc") {
26472
26999
  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="${escapeXml3(strokeCol)}" stroke-width="0.3"`}/>`);
@@ -26486,13 +27013,17 @@ function cellContentExtent(cell) {
26486
27013
  for (const s of m.segs) ext = Math.max(ext, s.vertpos + s.textheight);
26487
27014
  const baseV = m.segs[0]?.vertpos ?? 0;
26488
27015
  for (const o of m.objs) {
26489
- if (o.inline) continue;
27016
+ if (o.inline) {
27017
+ const h = o.tag === "tbl" ? Math.max(o.height, measureTableHeight(o.el)) : o.height;
27018
+ ext = Math.max(ext, baseV + h);
27019
+ continue;
27020
+ }
26490
27021
  const pos = findChildByLocalName(o.el, "pos");
26491
27022
  if ((pos?.getAttribute("vertRelTo") ?? "PARA") !== "PARA") continue;
26492
27023
  const om = findChildByLocalName(o.el, "outMargin");
26493
- const pushed = baseV - (num2(om, "top") + o.height + num2(om, "bottom"));
27024
+ const pushed = baseV - (num3(om, "top") + o.height + num3(om, "bottom"));
26494
27025
  const anchor = pushed >= -100 ? pushed : baseV;
26495
- ext = Math.max(ext, anchor + num2(om, "top") + num2(pos, "vertOffset") + o.height);
27026
+ ext = Math.max(ext, anchor + num3(om, "top") + num3(pos, "vertOffset") + o.height);
26496
27027
  }
26497
27028
  }
26498
27029
  return ext;
@@ -26503,8 +27034,8 @@ function edgeLine(x1, y1, x2, y2, e) {
26503
27034
  }
26504
27035
  function collectCells(tbl) {
26505
27036
  const inMargin = findChildByLocalName(tbl, "inMargin");
26506
- const defL = num2(inMargin, "left", 141), defR = num2(inMargin, "right", 141);
26507
- const defT = num2(inMargin, "top", 141), defB = num2(inMargin, "bottom", 141);
27037
+ const defL = num3(inMargin, "left", 141), defR = num3(inMargin, "right", 141);
27038
+ const defT = num3(inMargin, "top", 141), defB = num3(inMargin, "bottom", 141);
26508
27039
  const cells = [];
26509
27040
  for (const tr of elements2(tbl)) {
26510
27041
  if (ln2(tr) !== "tr") continue;
@@ -26517,18 +27048,18 @@ function collectCells(tbl) {
26517
27048
  if (!addr || !csz) continue;
26518
27049
  cells.push({
26519
27050
  el: tc,
26520
- ca: num2(addr, "colAddr"),
26521
- ra: num2(addr, "rowAddr"),
26522
- cs: Math.max(1, num2(span, "colSpan", 1)),
26523
- rs: Math.max(1, num2(span, "rowSpan", 1)),
26524
- w: num2(csz, "width"),
26525
- h: num2(csz, "height"),
27051
+ ca: num3(addr, "colAddr"),
27052
+ ra: num3(addr, "rowAddr"),
27053
+ cs: Math.max(1, num3(span, "colSpan", 1)),
27054
+ rs: Math.max(1, num3(span, "rowSpan", 1)),
27055
+ w: num3(csz, "width"),
27056
+ h: num3(csz, "height"),
26526
27057
  bfId: tc.getAttribute("borderFillIDRef"),
26527
27058
  sub: findChildByLocalName(tc, "subList"),
26528
- marginL: cm ? num2(cm, "left", defL) : defL,
26529
- marginR: cm ? num2(cm, "right", defR) : defR,
26530
- marginT: cm ? num2(cm, "top", defT) : defT,
26531
- marginB: cm ? num2(cm, "bottom", defB) : defB
27059
+ marginL: cm ? num3(cm, "left", defL) : defL,
27060
+ marginR: cm ? num3(cm, "right", defR) : defR,
27061
+ marginT: cm ? num3(cm, "top", defT) : defT,
27062
+ marginB: cm ? num3(cm, "bottom", defB) : defB
26532
27063
  });
26533
27064
  }
26534
27065
  }
@@ -26558,7 +27089,7 @@ function drawTable(tbl, tx, ty, ctx, depth) {
26558
27089
  const nCols = Math.max(...cells.map((c) => c.ca + c.cs));
26559
27090
  const nRows = Math.max(...cells.map((c) => c.ra + c.rs));
26560
27091
  const colCons = cells.map((c) => ({ a: c.ca, b: c.ca + c.cs, size: c.w }));
26561
- const colX = solveBoundaries(colCons, nCols, num2(tblSz, "width") || void 0);
27092
+ const colX = solveBoundaries(colCons, nCols, num3(tblSz, "width") || void 0);
26562
27093
  const rowH = solveRowHeights(
26563
27094
  cells.map((c) => ({ rowAddr: c.ra, rowSpan: c.rs, height: c.h, contentH: c.rs === 1 ? cellContentExtent(c) : void 0 })),
26564
27095
  nRows
@@ -26610,7 +27141,7 @@ function imageSymbol(loaded, ctx) {
26610
27141
  }
26611
27142
  function drawPic(pic, x, y, ctx) {
26612
27143
  const sz = findChildByLocalName(pic, "sz");
26613
- const w = num2(sz, "width", 5669), h = num2(sz, "height", 5669);
27144
+ const w = num3(sz, "width", 5669), h = num3(sz, "height", 5669);
26614
27145
  const img = findFirst(pic, "img");
26615
27146
  const ref = img?.getAttribute("binaryItemIDRef");
26616
27147
  const loaded = ref != null ? ctx.images.get(ref) : void 0;
@@ -26623,11 +27154,11 @@ function drawPic(pic, x, y, ctx) {
26623
27154
  const clip = findChildByLocalName(pic, "imgClip");
26624
27155
  const imgDim = findChildByLocalName(pic, "imgDim");
26625
27156
  const orgSz = findChildByLocalName(pic, "orgSz");
26626
- const dimW = num2(imgDim, "dimwidth"), dimH = num2(imgDim, "dimheight");
26627
- const refW = dimW > 0 ? dimW : num2(orgSz, "width");
26628
- const refH = dimH > 0 ? dimH : num2(orgSz, "height");
26629
- const cl = num2(clip, "left"), ct = num2(clip, "top");
26630
- const cr = num2(clip, "right", refW), cb = num2(clip, "bottom", refH);
27157
+ const dimW = num3(imgDim, "dimwidth"), dimH = num3(imgDim, "dimheight");
27158
+ const refW = dimW > 0 ? dimW : num3(orgSz, "width");
27159
+ const refH = dimH > 0 ? dimH : num3(orgSz, "height");
27160
+ const cl = num3(clip, "left"), ct = num3(clip, "top");
27161
+ const cr = num3(clip, "right", refW), cb = num3(clip, "bottom", refH);
26631
27162
  const cropped = refW > 0 && refH > 0 && clip != null && (cl > 0 || ct > 0 || cr < refW || cb < refH) && cr > cl && cb > ct;
26632
27163
  const symId = imageSymbol(loaded, ctx);
26633
27164
  if (cropped) {
@@ -26647,28 +27178,89 @@ function sniffMime(name, bytes) {
26647
27178
  if (lower.endsWith(".svg")) return "image/svg+xml";
26648
27179
  return "image/jpeg";
26649
27180
  }
27181
+ function readSectionGeom(root) {
27182
+ const pagePr = findFirst(root, "pagePr");
27183
+ const margin = pagePr ? findChildByLocalName(pagePr, "margin") : null;
27184
+ let PW = num3(pagePr, "width", 59528), PH = num3(pagePr, "height", 84188);
27185
+ if (pagePr?.getAttribute("landscape") === "NARROWLY" && PW < PH) {
27186
+ const t = PW;
27187
+ PW = PH;
27188
+ PH = t;
27189
+ }
27190
+ const ML = num3(margin, "left", 8504);
27191
+ const MT = num3(margin, "top", 5668) + num3(margin, "header", 0);
27192
+ const BODY_H = PH - MT - num3(margin, "bottom", 4252) - num3(margin, "footer", 0);
27193
+ const BODY_W = PW - ML - num3(margin, "right", 8504);
27194
+ return { PW, PH, ML, MT, BODY_W, BODY_H };
27195
+ }
27196
+ function renderSectionToPages(root, geom, ctxBase, hasCache, reflowMode) {
27197
+ const { PW, PH, ML, MT, BODY_W, BODY_H } = geom;
27198
+ if (!hasCache) reflowSection(root, ctxBase.styles, { BODY_W, BODY_H }, reflowMode);
27199
+ const colPr = findFirst(root, "colPr");
27200
+ const multiCol = num3(colPr, "colCount", 1) > 1;
27201
+ const paraSegPages = /* @__PURE__ */ new Map();
27202
+ let nPages = 1;
27203
+ let maxTopV = 0;
27204
+ {
27205
+ let prevV = Number.NEGATIVE_INFINITY;
27206
+ let prevH = Number.NEGATIVE_INFINITY;
27207
+ let cur = 0;
27208
+ for (const p of elements2(root)) {
27209
+ if (ln2(p) !== "p") continue;
27210
+ const lsa = findChildByLocalName(p, "linesegarray");
27211
+ const segEls = lsa ? elements2(lsa).filter((s) => ln2(s) === "lineseg") : [];
27212
+ const pagesOf = [];
27213
+ let paraFirst = true;
27214
+ for (const s of segEls) {
27215
+ const v = num3(s, "vertpos");
27216
+ const h = num3(s, "horzpos");
27217
+ const brk = v < prevV ? !multiCol || h <= prevH : paraFirst && v === prevV && h <= prevH;
27218
+ if (brk) cur++;
27219
+ paraFirst = false;
27220
+ pagesOf.push(cur);
27221
+ maxTopV = Math.max(maxTopV, v + num3(s, "textheight", 1e3));
27222
+ prevV = v;
27223
+ prevH = h;
27224
+ }
27225
+ paraSegPages.set(p, pagesOf);
27226
+ nPages = Math.max(nPages, cur + 1);
27227
+ }
27228
+ }
27229
+ const ctx = {
27230
+ ...ctxBase,
27231
+ pages: Array.from({ length: nPages }, () => []),
27232
+ page: 0,
27233
+ geom
27234
+ };
27235
+ for (const p of elements2(root)) {
27236
+ if (ln2(p) !== "p") continue;
27237
+ drawPara(p, ML, MT, BODY_W, ctx, 0, paraSegPages.get(p));
27238
+ }
27239
+ const pageH = nPages === 1 ? Math.max(PH, MT + maxTopV + 2e3) : PH;
27240
+ return { pages: ctx.pages, pageH };
27241
+ }
26650
27242
  async function renderHwpxToSvg(input, options) {
26651
27243
  const maxImg = options?.maxImageBytes ?? 40 * 1024 * 1024;
26652
27244
  const ab = input instanceof Uint8Array ? input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength) : input;
26653
27245
  precheckZipSize(ab, MAX_DECOMPRESS_SIZE, MAX_ZIP_ENTRIES);
26654
27246
  let zip;
26655
27247
  try {
26656
- zip = await JSZip12.loadAsync(input);
27248
+ zip = await JSZip13.loadAsync(input);
26657
27249
  } catch {
26658
27250
  throw new KordocError("HWPX(ZIP) \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4 \u2014 \uB80C\uB354\uB294 HWPX\uB9CC \uC9C0\uC6D0");
26659
27251
  }
26660
- const secFile = zip.file("Contents/section0.xml") ?? zip.file(/Contents\/section\d+\.xml$/i).sort((a, b) => a.name.localeCompare(b.name))[0];
26661
- if (!secFile) throw new KordocError("Contents/section0.xml \uC5C6\uC74C \u2014 HWPX\uAC00 \uC544\uB2C8\uAC70\uB098 \uC190\uC0C1\uB428");
26662
- const secXml = await secFile.async("string");
26663
- if (secXml.length > MAX_DECOMPRESS_SIZE) throw new KordocError("\uC139\uC158 XML\uC774 \uD5C8\uC6A9 \uD06C\uAE30\uB97C \uCD08\uACFC");
26664
- const hasCache = /<(?:[A-Za-z][\w.-]*:)?linesegarray[\s/>]/.test(secXml);
26665
- if (!hasCache && !options?.reflow) {
26666
- 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)");
26667
- }
27252
+ const secFiles = zip.file(/Contents\/section\d+\.xml$/i).sort((a, b) => a.name.localeCompare(b.name));
27253
+ if (secFiles.length === 0) throw new KordocError("Contents/section0.xml \uC5C6\uC74C \u2014 HWPX\uAC00 \uC544\uB2C8\uAC70\uB098 \uC190\uC0C1\uB428");
26668
27254
  const warnings = [];
26669
27255
  const headFile = zip.file("Contents/header.xml") ?? zip.file("Contents/head.xml");
26670
27256
  const styles = headFile ? parseRenderStyles(await headFile.async("string")) : { charPr: /* @__PURE__ */ new Map(), paraAlign: /* @__PURE__ */ new Map(), paraGeom: /* @__PURE__ */ new Map(), borderFill: /* @__PURE__ */ new Map() };
26671
27257
  if (!headFile) warnings.push("header.xml \uC5C6\uC74C \u2014 \uAE30\uBCF8 \uC2A4\uD0C0\uC77C\uB85C \uB80C\uB354");
27258
+ const secXmls = [];
27259
+ for (const f of secFiles) {
27260
+ const xml = await f.async("string");
27261
+ if (xml.length > MAX_DECOMPRESS_SIZE) throw new KordocError("\uC139\uC158 XML\uC774 \uD5C8\uC6A9 \uD06C\uAE30\uB97C \uCD08\uACFC");
27262
+ secXmls.push(xml);
27263
+ }
26672
27264
  const binmap = /* @__PURE__ */ new Map();
26673
27265
  const hpf = zip.file(/content\.hpf$/i)[0];
26674
27266
  if (hpf) {
@@ -26680,7 +27272,7 @@ async function renderHwpxToSvg(input, options) {
26680
27272
  const MAX_TOTAL_IMAGE_BYTES = 128 * 1024 * 1024;
26681
27273
  const images = /* @__PURE__ */ new Map();
26682
27274
  const refs = /* @__PURE__ */ new Set();
26683
- for (const m of secXml.matchAll(/binaryItemIDRef="([^"]+)"/g)) refs.add(m[1]);
27275
+ for (const xml of secXmls) for (const m of xml.matchAll(/binaryItemIDRef="([^"]+)"/g)) refs.add(m[1]);
26684
27276
  let totalImgBytes = 0;
26685
27277
  for (const ref of refs) {
26686
27278
  if (images.size >= MAX_IMAGE_REFS) {
@@ -26707,48 +27299,7 @@ async function renderHwpxToSvg(input, options) {
26707
27299
  totalImgBytes += bytes.length;
26708
27300
  images.set(ref, { dataUri: `data:${sniffMime(href, bytes)};base64,${Buffer.from(bytes).toString("base64")}` });
26709
27301
  }
26710
- const doc = createXmlParser().parseFromString(secXml, "text/xml");
26711
- const root = doc.documentElement;
26712
- if (!root) throw new KordocError("\uC139\uC158 XML \uD30C\uC2F1 \uC2E4\uD328");
26713
- const pagePr = findFirst(root, "pagePr");
26714
- const margin = pagePr ? findChildByLocalName(pagePr, "margin") : null;
26715
- const PW = num2(pagePr, "width", 59528), PH = num2(pagePr, "height", 84188);
26716
- const ML = num2(margin, "left", 8504);
26717
- const MT = num2(margin, "top", 5668) + num2(margin, "header", 0);
26718
- const BODY_H = PH - MT - num2(margin, "bottom", 4252) - num2(margin, "footer", 0);
26719
- const BODY_W = PW - ML - num2(margin, "right", 8504);
26720
- if (!hasCache) reflowSection(root, styles, { BODY_W, BODY_H }, options?.reflowMode ?? "keep");
26721
- const colPr = findFirst(root, "colPr");
26722
- const multiCol = num2(colPr, "colCount", 1) > 1;
26723
- const paraSegPages = /* @__PURE__ */ new Map();
26724
- let nPages = 1;
26725
- let maxTopV = 0;
26726
- {
26727
- let prevV = Number.NEGATIVE_INFINITY;
26728
- let prevH = Number.NEGATIVE_INFINITY;
26729
- let cur = 0;
26730
- for (const p of elements2(root)) {
26731
- if (ln2(p) !== "p") continue;
26732
- const lsa = findChildByLocalName(p, "linesegarray");
26733
- const segEls = lsa ? elements2(lsa).filter((s) => ln2(s) === "lineseg") : [];
26734
- const pagesOf = [];
26735
- for (const s of segEls) {
26736
- const v = num2(s, "vertpos");
26737
- const h = num2(s, "horzpos");
26738
- if (v < prevV && (!multiCol || h <= prevH)) cur++;
26739
- pagesOf.push(cur);
26740
- maxTopV = Math.max(maxTopV, v + num2(s, "textheight", 1e3));
26741
- prevV = v;
26742
- prevH = h;
26743
- }
26744
- paraSegPages.set(p, pagesOf);
26745
- nPages = Math.max(nPages, cur + 1);
26746
- }
26747
- }
26748
- const ctx = {
26749
- pages: Array.from({ length: nPages }, () => []),
26750
- page: 0,
26751
- geom: { PW, PH, ML, MT, BODY_W, BODY_H },
27302
+ const ctxBase = {
26752
27303
  styles,
26753
27304
  images,
26754
27305
  defs: [],
@@ -26757,23 +27308,57 @@ async function renderHwpxToSvg(input, options) {
26757
27308
  warned: /* @__PURE__ */ new Set(),
26758
27309
  stats: { texts: 0, images: 0, tables: 0 }
26759
27310
  };
26760
- for (const p of elements2(root)) {
26761
- if (ln2(p) !== "p") continue;
26762
- drawPara(p, ML, MT, BODY_W, ctx, 0, paraSegPages.get(p));
27311
+ const rendered = [];
27312
+ let noCacheSkipped = false;
27313
+ for (let si = 0; si < secXmls.length; si++) {
27314
+ const secXml = secXmls[si];
27315
+ const hasCache = /<(?:[A-Za-z][\w.-]*:)?linesegarray[\s/>]/.test(secXml);
27316
+ if (!hasCache && !options?.reflow) {
27317
+ noCacheSkipped = true;
27318
+ warnings.push(`\uAD6C\uC5ED ${si}: \uC870\uD310 \uCE90\uC2DC \uC5C6\uC74C \u2014 reflow \uC635\uC158 \uD544\uC694, \uC0DD\uB7B5`);
27319
+ continue;
27320
+ }
27321
+ const doc = createXmlParser().parseFromString(secXml, "text/xml");
27322
+ const root = doc.documentElement;
27323
+ if (!root) {
27324
+ warnings.push(`\uAD6C\uC5ED ${si} XML \uD30C\uC2F1 \uC2E4\uD328 \u2014 \uC0DD\uB7B5`);
27325
+ continue;
27326
+ }
27327
+ const geom = readSectionGeom(root);
27328
+ const { pages, pageH } = renderSectionToPages(root, geom, ctxBase, hasCache, options?.reflowMode ?? "keep");
27329
+ rendered.push({ pages, PW: geom.PW, pageH, clipId: `pgclip${si}` });
27330
+ }
27331
+ if (rendered.length === 0) {
27332
+ if (noCacheSkipped) {
27333
+ 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)");
27334
+ }
27335
+ throw new KordocError("\uB80C\uB354\uD560 \uAD6C\uC5ED\uC774 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 HWPX\uAC00 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
26763
27336
  }
26764
- const pageH = nPages === 1 ? Math.max(PH, MT + maxTopV + 2e3) : PH;
26765
27337
  const GAP = 2400;
26766
- const totalH = nPages * pageH + (nPages - 1) * GAP;
26767
- const pagesSvg = ctx.pages.map(
26768
- (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)">
27338
+ const clipDefs = [];
27339
+ const groups = [];
27340
+ let y = 0;
27341
+ let maxPW = 0;
27342
+ let pageNo = 0;
27343
+ for (const rs of rendered) {
27344
+ maxPW = Math.max(maxPW, rs.PW);
27345
+ clipDefs.push(`<clipPath id="${rs.clipId}"><rect x="0" y="0" width="${pt(rs.PW)}" height="${pt(rs.pageH)}"/></clipPath>`);
27346
+ for (const buf of rs.pages) {
27347
+ pageNo++;
27348
+ groups.push(
27349
+ `<g data-page="${pageNo}" transform="translate(0 ${pt(y)})"><rect width="${pt(rs.PW)}" height="${pt(rs.pageH)}" fill="white" stroke="#c9c7c4" stroke-width="0.75"/><g clip-path="url(#${rs.clipId})">
26769
27350
  ${buf.join("\n")}
26770
27351
  </g></g>`
26771
- ).join("\n");
26772
- 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">
26773
- <defs><clipPath id="pgclip"><rect x="0" y="0" width="${pt(PW)}" height="${pt(pageH)}"/></clipPath>${ctx.defs.join("")}</defs>
26774
- ${pagesSvg}
27352
+ );
27353
+ y += rs.pageH + GAP;
27354
+ }
27355
+ }
27356
+ const totalH = Math.max(0, y - GAP);
27357
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${pt(maxPW)} ${pt(totalH)}" width="${pt(maxPW)}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">
27358
+ <defs>${clipDefs.join("")}${ctxBase.defs.join("")}</defs>
27359
+ ${groups.join("\n")}
26775
27360
  </svg>`;
26776
- return { svg, width: Math.round(PW) / 100, height: Math.round(totalH) / 100, pageCount: nPages, warnings, stats: ctx.stats };
27361
+ return { svg, width: Math.round(maxPW) / 100, height: Math.round(totalH) / 100, pageCount: pageNo, warnings, stats: ctxBase.stats };
26777
27362
  }
26778
27363
 
26779
27364
  // src/index.ts
@@ -26863,7 +27448,7 @@ async function parseHwp(buffer, options) {
26863
27448
  async function parsePdf(buffer, options) {
26864
27449
  let parsePdfDocument;
26865
27450
  try {
26866
- const mod = await import("./parser-GWL72XYS.js");
27451
+ const mod = await import("./parser-5D6BQYTX.js");
26867
27452
  parsePdfDocument = mod.parsePdfDocument;
26868
27453
  } catch {
26869
27454
  return {
@@ -26978,6 +27563,7 @@ export {
26978
27563
  fillWithUniqueGuard,
26979
27564
  fitRatioForFewerLines,
26980
27565
  formatFillValue,
27566
+ hwpxToProfile,
26981
27567
  inferFieldType,
26982
27568
  isHwpxFile,
26983
27569
  isLabelCell,