kordoc 3.17.0 → 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 (48) hide show
  1. package/README.md +5 -0
  2. package/dist/{-3KPDRR3A.js → -OUCPI6B3.js} +9 -7
  3. package/dist/{chunk-NKICMZ7F.js → chunk-4YEZTZAR.js} +2 -2
  4. package/dist/{chunk-EL2Y4BL2.cjs → chunk-5HDBLRVP.cjs} +45 -9
  5. package/dist/chunk-5HDBLRVP.cjs.map +1 -0
  6. package/dist/{chunk-HVEEFHN7.js → chunk-6Y4KKPC3.js} +2 -2
  7. package/dist/{chunk-E6NQWOFQ.js → chunk-GIXXV52X.js} +39 -3
  8. package/dist/chunk-GIXXV52X.js.map +1 -0
  9. package/dist/{chunk-VUS6LRVY.js → chunk-PWHM3KAA.js} +535 -83
  10. package/dist/chunk-PWHM3KAA.js.map +1 -0
  11. package/dist/{chunk-TDIDUEBH.js → chunk-VP34RO24.js} +3 -3
  12. package/dist/{chunk-WHMNPQXW.js → chunk-YWIYGXLI.js} +39 -3
  13. package/dist/chunk-YWIYGXLI.js.map +1 -0
  14. package/dist/{chunk-SADQWCWR.js → chunk-ZTUQYITH.js} +3 -3
  15. package/dist/cli.js +18 -15
  16. package/dist/cli.js.map +1 -1
  17. package/dist/index.cjs +862 -411
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +118 -1
  20. package/dist/index.d.ts +118 -1
  21. package/dist/index.js +592 -141
  22. package/dist/index.js.map +1 -1
  23. package/dist/mcp.js +22 -10
  24. package/dist/mcp.js.map +1 -1
  25. package/dist/{parser-LHBXIKXX.js → parser-5D6BQYTX.js} +2 -2
  26. package/dist/{parser-CYJMOXUR.js → parser-NZAE7PBE.js} +3 -3
  27. package/dist/{parser-I5GC4QQ5.cjs → parser-S34YR2SH.cjs} +14 -14
  28. package/dist/{parser-I5GC4QQ5.cjs.map → parser-S34YR2SH.cjs.map} +1 -1
  29. package/dist/render-HRAW7ASR.js +10 -0
  30. package/dist/seal-CK64CXWO.js +10 -0
  31. package/dist/{watch-VT7BSLNI.js → watch-2EFXBRYK.js} +7 -7
  32. package/package.json +1 -1
  33. package/dist/chunk-E6NQWOFQ.js.map +0 -1
  34. package/dist/chunk-EL2Y4BL2.cjs.map +0 -1
  35. package/dist/chunk-VUS6LRVY.js.map +0 -1
  36. package/dist/chunk-WHMNPQXW.js.map +0 -1
  37. package/dist/render-HY7KKYPV.js +0 -10
  38. package/dist/seal-ITGYHENO.js +0 -10
  39. /package/dist/{-3KPDRR3A.js.map → -OUCPI6B3.js.map} +0 -0
  40. /package/dist/{chunk-NKICMZ7F.js.map → chunk-4YEZTZAR.js.map} +0 -0
  41. /package/dist/{chunk-HVEEFHN7.js.map → chunk-6Y4KKPC3.js.map} +0 -0
  42. /package/dist/{chunk-TDIDUEBH.js.map → chunk-VP34RO24.js.map} +0 -0
  43. /package/dist/{chunk-SADQWCWR.js.map → chunk-ZTUQYITH.js.map} +0 -0
  44. /package/dist/{parser-LHBXIKXX.js.map → parser-5D6BQYTX.js.map} +0 -0
  45. /package/dist/{parser-CYJMOXUR.js.map → parser-NZAE7PBE.js.map} +0 -0
  46. /package/dist/{render-HY7KKYPV.js.map → render-HRAW7ASR.js.map} +0 -0
  47. /package/dist/{seal-ITGYHENO.js.map → seal-CK64CXWO.js.map} +0 -0
  48. /package/dist/{watch-VT7BSLNI.js.map → watch-2EFXBRYK.js.map} +0 -0
@@ -8,9 +8,10 @@ import {
8
8
  blocksToMarkdown,
9
9
  buildTable,
10
10
  convertTableToText,
11
+ dedupeRunningHeaders,
11
12
  flattenLayoutTables,
12
13
  mapPuaText
13
- } from "./chunk-WHMNPQXW.js";
14
+ } from "./chunk-YWIYGXLI.js";
14
15
  import {
15
16
  detectFormat,
16
17
  detectOle2Format,
@@ -31,12 +32,12 @@ import {
31
32
  paraTextPureT,
32
33
  patchZipEntries,
33
34
  scanSectionXml
34
- } from "./chunk-TDIDUEBH.js";
35
+ } from "./chunk-VP34RO24.js";
35
36
  import {
36
37
  SPACE_EM_FIXED,
37
38
  charWidthEm1000,
38
39
  fitRatioForFewerLines
39
- } from "./chunk-SADQWCWR.js";
40
+ } from "./chunk-ZTUQYITH.js";
40
41
  import {
41
42
  MAX_DECOMPRESS_SIZE,
42
43
  MAX_XML_DEPTH,
@@ -47,7 +48,7 @@ import {
47
48
  createXmlParser,
48
49
  extractTextFromNode,
49
50
  findChildByLocalName
50
- } from "./chunk-NKICMZ7F.js";
51
+ } from "./chunk-4YEZTZAR.js";
51
52
  import {
52
53
  KordocError,
53
54
  classifyError,
@@ -58,7 +59,7 @@ import {
58
59
  sanitizeHref,
59
60
  stripDtd,
60
61
  toArrayBuffer
61
- } from "./chunk-HVEEFHN7.js";
62
+ } from "./chunk-6Y4KKPC3.js";
62
63
 
63
64
  // src/index.ts
64
65
  import { readFile } from "fs/promises";
@@ -2274,11 +2275,11 @@ function formatRoman(n, upper) {
2274
2275
  const values = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
2275
2276
  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"];
2276
2277
  let result = "";
2277
- let num = n;
2278
+ let num2 = n;
2278
2279
  for (let i = 0; i < values.length; i++) {
2279
- while (num >= values[i]) {
2280
+ while (num2 >= values[i]) {
2280
2281
  result += symbols[i];
2281
- num -= values[i];
2282
+ num2 -= values[i];
2282
2283
  }
2283
2284
  }
2284
2285
  return result;
@@ -2286,11 +2287,11 @@ function formatRoman(n, upper) {
2286
2287
  function formatLatin(n, upper) {
2287
2288
  if (n <= 0) return "";
2288
2289
  let result = "";
2289
- let num = n;
2290
- while (num > 0) {
2291
- num--;
2292
- result = String.fromCharCode((upper ? 65 : 97) + num % 26) + result;
2293
- num = Math.floor(num / 26);
2290
+ let num2 = n;
2291
+ while (num2 > 0) {
2292
+ num2--;
2293
+ result = String.fromCharCode((upper ? 65 : 97) + num2 % 26) + result;
2294
+ num2 = Math.floor(num2 / 26);
2294
2295
  }
2295
2296
  return result;
2296
2297
  }
@@ -2298,15 +2299,15 @@ function formatEastAsianNumber(n, digits, units, zero) {
2298
2299
  if (n === 0) return zero;
2299
2300
  if (n < 0 || n > 99999) return String(n);
2300
2301
  let result = "";
2301
- let num = n;
2302
+ let num2 = n;
2302
2303
  let unit = 0;
2303
- while (num > 0) {
2304
- const d = num % 10;
2304
+ while (num2 > 0) {
2305
+ const d = num2 % 10;
2305
2306
  if (d > 0) {
2306
2307
  const digitStr = d === 1 && unit > 0 ? "" : digits[d];
2307
2308
  result = digitStr + units[unit] + result;
2308
2309
  }
2309
- num = Math.floor(num / 10);
2310
+ num2 = Math.floor(num2 / 10);
2310
2311
  unit++;
2311
2312
  }
2312
2313
  return result;
@@ -2353,8 +2354,8 @@ function expandNumberingFormat(formatStr, counters, numbering) {
2353
2354
  const idx = levelRef - 1;
2354
2355
  const counterVal = counters[idx] ?? 0;
2355
2356
  const start = numbering.startNumbers[idx] ?? 1;
2356
- const num = counterVal > 0 ? start - 1 + counterVal : start;
2357
- result += formatNumber(num, headFormatToNumFmt(numbering.numberFormats[idx] ?? 0));
2357
+ const num2 = counterVal > 0 ? start - 1 + counterVal : start;
2358
+ result += formatNumber(num2, headFormatToNumFmt(numbering.numberFormats[idx] ?? 0));
2358
2359
  i += 2;
2359
2360
  continue;
2360
2361
  }
@@ -2503,6 +2504,141 @@ function extractHwp5ImagesLenient(lcfb, blocks, warnings) {
2503
2504
  return resolveImageBlocks(binDataMap, blocks, warnings);
2504
2505
  }
2505
2506
 
2507
+ // src/image/transcode.ts
2508
+ import { deflateSync } from "zlib";
2509
+ var CRC_TABLE = (() => {
2510
+ const table = new Uint32Array(256);
2511
+ for (let n = 0; n < 256; n++) {
2512
+ let c = n;
2513
+ for (let k = 0; k < 8; k++) {
2514
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
2515
+ }
2516
+ table[n] = c >>> 0;
2517
+ }
2518
+ return table;
2519
+ })();
2520
+ function crc32(bytes) {
2521
+ let c = 4294967295;
2522
+ for (let i = 0; i < bytes.length; i++) {
2523
+ c = CRC_TABLE[(c ^ bytes[i]) & 255] ^ c >>> 8;
2524
+ }
2525
+ return (c ^ 4294967295) >>> 0;
2526
+ }
2527
+ var BI_RGB = 0;
2528
+ var MAX_DIM = 32767;
2529
+ var MAX_PIXELS = 64e6;
2530
+ function bmpToPng(bmp) {
2531
+ if (bmp.length < 54) return null;
2532
+ if (bmp[0] !== 66 || bmp[1] !== 77) return null;
2533
+ const dv = new DataView(bmp.buffer, bmp.byteOffset, bmp.byteLength);
2534
+ const dataOffset = dv.getUint32(10, true);
2535
+ const headerSize = dv.getUint32(14, true);
2536
+ if (headerSize < 40) return null;
2537
+ const width = dv.getInt32(18, true);
2538
+ const rawHeight = dv.getInt32(22, true);
2539
+ const bitCount = dv.getUint16(28, true);
2540
+ const compression = dv.getUint32(30, true);
2541
+ if (compression !== BI_RGB) return null;
2542
+ if (bitCount !== 24 && bitCount !== 32) return null;
2543
+ if (width <= 0 || rawHeight === 0) return null;
2544
+ if (width > MAX_DIM || Math.abs(rawHeight) > MAX_DIM) return null;
2545
+ const topDown = rawHeight < 0;
2546
+ const height = Math.abs(rawHeight);
2547
+ if (width * height > MAX_PIXELS) return null;
2548
+ const bytesPerPixel = bitCount >> 3;
2549
+ const rowStride = width * bytesPerPixel + 3 & ~3;
2550
+ if (dataOffset + rowStride * height > bmp.length) return null;
2551
+ const rgba = new Uint8Array(width * height * 4);
2552
+ let anyAlpha = 0;
2553
+ for (let y = 0; y < height; y++) {
2554
+ const srcRow = topDown ? y : height - 1 - y;
2555
+ let src = dataOffset + srcRow * rowStride;
2556
+ let dst = y * width * 4;
2557
+ for (let x = 0; x < width; x++) {
2558
+ rgba[dst] = bmp[src + 2];
2559
+ rgba[dst + 1] = bmp[src + 1];
2560
+ rgba[dst + 2] = bmp[src];
2561
+ const a = bitCount === 32 ? bmp[src + 3] : 255;
2562
+ rgba[dst + 3] = a;
2563
+ anyAlpha |= a;
2564
+ src += bytesPerPixel;
2565
+ dst += 4;
2566
+ }
2567
+ }
2568
+ if (bitCount === 32 && anyAlpha === 0) {
2569
+ for (let i = 3; i < rgba.length; i += 4) rgba[i] = 255;
2570
+ }
2571
+ return encodePng(width, height, rgba);
2572
+ }
2573
+ var PNG_SIGNATURE = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10);
2574
+ function chunk(type, data) {
2575
+ const body = new Uint8Array(4 + data.length);
2576
+ body[0] = type.charCodeAt(0);
2577
+ body[1] = type.charCodeAt(1);
2578
+ body[2] = type.charCodeAt(2);
2579
+ body[3] = type.charCodeAt(3);
2580
+ body.set(data, 4);
2581
+ const out = new Uint8Array(8 + data.length + 4);
2582
+ const dv = new DataView(out.buffer);
2583
+ dv.setUint32(0, data.length, false);
2584
+ out.set(body, 4);
2585
+ dv.setUint32(8 + data.length, crc32(body), false);
2586
+ return out;
2587
+ }
2588
+ function encodePng(width, height, rgba) {
2589
+ const ihdr = new Uint8Array(13);
2590
+ const iv = new DataView(ihdr.buffer);
2591
+ iv.setUint32(0, width, false);
2592
+ iv.setUint32(4, height, false);
2593
+ ihdr[8] = 8;
2594
+ ihdr[9] = 6;
2595
+ const stride = width * 4;
2596
+ const raw = new Uint8Array((stride + 1) * height);
2597
+ for (let y = 0; y < height; y++) {
2598
+ const rowStart = y * (stride + 1);
2599
+ raw[rowStart] = 0;
2600
+ raw.set(rgba.subarray(y * stride, y * stride + stride), rowStart + 1);
2601
+ }
2602
+ const idat = deflateSync(raw);
2603
+ const ihdrChunk = chunk("IHDR", ihdr);
2604
+ const idatChunk = chunk("IDAT", idat);
2605
+ const iendChunk = chunk("IEND", new Uint8Array(0));
2606
+ const out = new Uint8Array(PNG_SIGNATURE.length + ihdrChunk.length + idatChunk.length + iendChunk.length);
2607
+ let o = 0;
2608
+ out.set(PNG_SIGNATURE, o);
2609
+ o += PNG_SIGNATURE.length;
2610
+ out.set(ihdrChunk, o);
2611
+ o += ihdrChunk.length;
2612
+ out.set(idatChunk, o);
2613
+ o += idatChunk.length;
2614
+ out.set(iendChunk, o);
2615
+ return out;
2616
+ }
2617
+ var MAX_INLINE_MD_BYTES = 4 * 1024 * 1024;
2618
+ function escapeRegExp(s) {
2619
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2620
+ }
2621
+ function inlineImagesIntoMarkdown(markdown, images, opts) {
2622
+ const compress = opts?.compress !== false;
2623
+ let out = markdown;
2624
+ for (const img of images) {
2625
+ let bytes = img.data;
2626
+ let mime = img.mimeType;
2627
+ if (compress && mime === "image/bmp") {
2628
+ const png = bmpToPng(img.data);
2629
+ if (png) {
2630
+ bytes = png;
2631
+ mime = "image/png";
2632
+ }
2633
+ }
2634
+ const base64 = Buffer.from(bytes).toString("base64");
2635
+ const dataUri = `data:${mime};base64,${base64}`;
2636
+ const re = new RegExp(`!\\[image\\]\\((?:images/)?${escapeRegExp(img.filename)}\\)`, "g");
2637
+ out = out.replace(re, () => `![image](${dataUri})`);
2638
+ }
2639
+ return out;
2640
+ }
2641
+
2506
2642
  // src/hwp5/aes.ts
2507
2643
  var S_BOX = new Uint8Array([
2508
2644
  99,
@@ -3551,12 +3687,25 @@ function parseHwp5Document(buffer, options) {
3551
3687
  }
3552
3688
  const blocks = [...doc.headerBlocks, ...bodyBlocks, ...doc.footerBlocks];
3553
3689
  const images = cfb ? extractHwp5Images(cfb.FileIndex, blocks, warnings) : extractHwp5ImagesLenient(lenientCfb, blocks, warnings);
3554
- const flatBlocks = flattenLayoutTables(blocks);
3690
+ let flatBlocks = flattenLayoutTables(blocks);
3691
+ if (options?.dedupeRunningHeaders) {
3692
+ const deduped = dedupeRunningHeaders(flatBlocks);
3693
+ const removed = flatBlocks.length - deduped.length;
3694
+ if (removed > 0) warnings.push({ message: `\uBC18\uBCF5 \uB7EC\uB2DD \uD5E4\uB354 ${removed}\uAC1C \uC81C\uAC70`, code: "HIDDEN_TEXT_FILTERED" });
3695
+ flatBlocks = deduped;
3696
+ }
3555
3697
  if (docInfo) {
3556
3698
  detectHwp5Headings(flatBlocks, docInfo);
3557
3699
  }
3558
3700
  const outline = flatBlocks.filter((b) => b.type === "heading" && b.level && b.text).map((b) => ({ level: b.level, text: b.text, pageNumber: b.pageNumber }));
3559
- const markdown = blocksToMarkdown(flatBlocks);
3701
+ let markdown = blocksToMarkdown(flatBlocks);
3702
+ if (options?.inlineImages && images.length > 0) {
3703
+ try {
3704
+ markdown = inlineImagesIntoMarkdown(markdown, images, { compress: true });
3705
+ } catch (inlineErr) {
3706
+ 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" });
3707
+ }
3708
+ }
3560
3709
  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 };
3561
3710
  }
3562
3711
  function parseDocInfoStream(cfb, compressed) {
@@ -3936,11 +4085,11 @@ function applyCtrlEffect(ctrl, records, ctx) {
3936
4085
  const attr = ctrl.data.readUInt32LE(4);
3937
4086
  const type = attr & 15;
3938
4087
  const format = attr >>> 4 & 255;
3939
- const num = ctx.doc.autoCounters.get(type) ?? 1;
3940
- ctx.doc.autoCounters.set(type, num + 1);
4088
+ const num2 = ctx.doc.autoCounters.get(type) ?? 1;
4089
+ ctx.doc.autoCounters.set(type, num2 + 1);
3941
4090
  const prefix = ctrl.data.length >= 14 ? wcharAt(ctrl.data, 12) : "";
3942
4091
  const suffix = ctrl.data.length >= 16 ? wcharAt(ctrl.data, 14) : "";
3943
- ctrl.inlineText = `${prefix}${formatNumber(num, shapeFormatToNumFmt(format))}${suffix}`;
4092
+ ctrl.inlineText = `${prefix}${formatNumber(num2, shapeFormatToNumFmt(format))}${suffix}`;
3944
4093
  }
3945
4094
  return;
3946
4095
  }
@@ -3948,8 +4097,8 @@ function applyCtrlEffect(ctrl, records, ctx) {
3948
4097
  if (ctrl.data.length >= 10) {
3949
4098
  const attr = ctrl.data.readUInt32LE(4);
3950
4099
  const type = attr & 15;
3951
- const num = ctrl.data.readUInt16LE(8);
3952
- if (num > 0) ctx.doc.autoCounters.set(type, num);
4100
+ const num2 = ctrl.data.readUInt16LE(8);
4101
+ if (num2 > 0) ctx.doc.autoCounters.set(type, num2);
3953
4102
  }
3954
4103
  return;
3955
4104
  }
@@ -4011,7 +4160,7 @@ function blocksPlainText(blocks, sep) {
4011
4160
  return parts.join(sep).trim();
4012
4161
  }
4013
4162
  function applyNoteEffect(ctrl, records, ctx, autoType) {
4014
- const num = ctx.doc.autoCounters.get(autoType) ?? 1;
4163
+ const num2 = ctx.doc.autoCounters.get(autoType) ?? 1;
4015
4164
  let before = "";
4016
4165
  let after = "";
4017
4166
  let shape = 0;
@@ -4022,11 +4171,11 @@ function applyNoteEffect(ctrl, records, ctx, autoType) {
4022
4171
  if (ctrl.data.length >= 16) {
4023
4172
  shape = ctrl.data.readUInt32LE(12) & 255;
4024
4173
  }
4025
- const formatted = formatNumber(num, shapeFormatToNumFmt(shape));
4174
+ const formatted = formatNumber(num2, shapeFormatToNumFmt(shape));
4026
4175
  const marker = before || after ? `${before}${formatted}${after}` : `${formatted})`;
4027
4176
  const content = blocksPlainText(parseListHeaderParagraphs(ctrl, records, ctx), " ");
4028
- if ((ctx.doc.autoCounters.get(autoType) ?? 1) <= num) {
4029
- ctx.doc.autoCounters.set(autoType, num + 1);
4177
+ if ((ctx.doc.autoCounters.get(autoType) ?? 1) <= num2) {
4178
+ ctx.doc.autoCounters.set(autoType, num2 + 1);
4030
4179
  }
4031
4180
  ctrl.inlineText = marker;
4032
4181
  if (content) ctrl.footnote = content.startsWith(marker) ? content : `${marker} ${content}`;
@@ -16564,9 +16713,9 @@ var MAX_ROWS2 = 1e4;
16564
16713
  var MAX_COLS2 = 200;
16565
16714
  function cleanNumericValue(raw) {
16566
16715
  if (!/^-?\d+\.\d+$/.test(raw)) return raw;
16567
- const num = parseFloat(raw);
16568
- if (!isFinite(num)) return raw;
16569
- const cleaned = parseFloat(num.toPrecision(15)).toString();
16716
+ const num2 = parseFloat(raw);
16717
+ if (!isFinite(num2)) return raw;
16718
+ const cleaned = parseFloat(num2.toPrecision(15)).toString();
16570
16719
  return cleaned;
16571
16720
  }
16572
16721
  function parseCellRef(ref) {
@@ -16909,17 +17058,17 @@ function decodeRk(rk) {
16909
17058
  const fDiv100 = (rk & 1) !== 0;
16910
17059
  const fInt = (rk & 2) !== 0;
16911
17060
  const val30 = rk >> 2;
16912
- let num;
17061
+ let num2;
16913
17062
  if (fInt) {
16914
- num = val30;
17063
+ num2 = val30;
16915
17064
  } else {
16916
17065
  const high32 = (rk & 4294967292) >>> 0;
16917
17066
  const buf = Buffer.alloc(8);
16918
17067
  buf.writeUInt32LE(0, 0);
16919
17068
  buf.writeUInt32LE(high32, 4);
16920
- num = buf.readDoubleLE(0);
17069
+ num2 = buf.readDoubleLE(0);
16921
17070
  }
16922
- return fDiv100 ? num / 100 : num;
17071
+ return fDiv100 ? num2 / 100 : num2;
16923
17072
  }
16924
17073
  function decodeMulRk(data) {
16925
17074
  if (data.length < 6) return null;
@@ -17619,9 +17768,9 @@ function nodeToLatex(el) {
17619
17768
  case "f": {
17620
17769
  const n = firstKid(el, "num");
17621
17770
  const d = firstKid(el, "den");
17622
- const num = n ? childrenToLatex(n) : "";
17771
+ const num2 = n ? childrenToLatex(n) : "";
17623
17772
  const den = d ? childrenToLatex(d) : "";
17624
- return "\\frac" + grp(num) + grp(den);
17773
+ return "\\frac" + grp(num2) + grp(den);
17625
17774
  }
17626
17775
  // 첨자
17627
17776
  case "sSup": {
@@ -20048,7 +20197,7 @@ function generateManifest(chartParts = []) {
20048
20197
  </opf:spine>
20049
20198
  </opf:package>`;
20050
20199
  }
20051
- function buildCharProperties(theme, gongmun, ratioVariants = []) {
20200
+ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
20052
20201
  let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
20053
20202
  if (gongmun) {
20054
20203
  body = gongmun.bodyHeight;
@@ -20080,6 +20229,7 @@ function buildCharProperties(theme, gongmun, ratioVariants = []) {
20080
20229
  charPr(rows.length + 3, body, true, true, 0, theme.body, r)
20081
20230
  );
20082
20231
  }
20232
+ rows.push(...extraCharPrXmls);
20083
20233
  return `<hh:charProperties itemCnt="${rows.length}">
20084
20234
  ${rows.join("\n")}
20085
20235
  </hh:charProperties>`;
@@ -20133,9 +20283,9 @@ ${heads}
20133
20283
  </hh:numbering>
20134
20284
  </hh:numberings>`;
20135
20285
  }
20136
- function generateHeaderXml(theme, gongmun, ratioVariants = []) {
20286
+ function generateHeaderXml(theme, gongmun, ratioVariants = [], remap = null) {
20137
20287
  const bodyFace = gongmun?.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD568\uCD08\uB86C\uBC14\uD0D5";
20138
- const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants);
20288
+ const charPropsXml = buildCharProperties(theme, gongmun, ratioVariants, remap?.charPrXmls ?? []);
20139
20289
  const paraPropsXml = buildParaProperties(gongmun);
20140
20290
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
20141
20291
  <hh:head xmlns:hh="${NS_HEAD}" xmlns:hp="${NS_PARA}" xmlns:hc="${NS_CORE}" version="1.4" secCnt="1">
@@ -20190,7 +20340,7 @@ function generateHeaderXml(theme, gongmun, ratioVariants = []) {
20190
20340
  </hh:font>
20191
20341
  </hh:fontface>
20192
20342
  </hh:fontfaces>
20193
- <hh:borderFills itemCnt="2">
20343
+ <hh:borderFills itemCnt="${2 + (remap?.borderFillXmls.length ?? 0)}">
20194
20344
  <hh:borderFill id="1" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
20195
20345
  <hh:slash type="NONE" Crooked="0" isCounter="0"/>
20196
20346
  <hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
@@ -20206,7 +20356,7 @@ function generateHeaderXml(theme, gongmun, ratioVariants = []) {
20206
20356
  <hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/>
20207
20357
  <hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/>
20208
20358
  <hh:bottomBorder type="SOLID" width="0.12 mm" color="#000000"/>
20209
- </hh:borderFill>
20359
+ </hh:borderFill>${remap && remap.borderFillXmls.length ? "\n" + remap.borderFillXmls.join("\n") : ""}
20210
20360
  </hh:borderFills>
20211
20361
  ${charPropsXml}
20212
20362
  <hh:tabProperties itemCnt="0"/>
@@ -20938,23 +21088,47 @@ var tableIdCounter = TABLE_ID_BASE;
20938
21088
  function nextTableId() {
20939
21089
  return ++tableIdCounter;
20940
21090
  }
20941
- function generateTable(rows, theme) {
21091
+ var warnedTables = /* @__PURE__ */ new Set();
21092
+ function warnProfileMismatch(tblId, tp, rowCnt, colCnt) {
21093
+ if (warnedTables.has(tblId)) return;
21094
+ warnedTables.add(tblId);
21095
+ console.warn(`[kordoc] format profile: table ${tp.rows}x${tp.cols} vs document ${rowCnt}x${colCnt} \u2014 \uD504\uB85C\uD544 \uBB34\uC2DC`);
21096
+ }
21097
+ function usableProfile(tp, tblId, rowCnt, colCnt) {
21098
+ if (!tp) return null;
21099
+ if (tp.rows !== rowCnt || tp.cols !== colCnt) {
21100
+ warnProfileMismatch(tblId, tp, rowCnt, colCnt);
21101
+ return null;
21102
+ }
21103
+ return tp;
21104
+ }
21105
+ function resolveColWidths(tp, colCnt, fallbackTotal) {
21106
+ if (tp?.colWidths && tp.colWidths.length === colCnt) return tp.colWidths;
21107
+ const w = tp?.width ? Math.floor(tp.width / colCnt) : Math.floor(fallbackTotal / colCnt);
21108
+ return Array(colCnt).fill(w);
21109
+ }
21110
+ function generateTable(rows, theme, tp = null) {
20942
21111
  const rowCnt = rows.length;
20943
21112
  const colCnt = Math.max(...rows.map((r) => r.length), 1);
20944
- const cellW = Math.floor(44e3 / colCnt);
20945
21113
  const cellH = 1500;
20946
- const tblW = cellW * colCnt;
20947
- const tblH = cellH * rowCnt;
20948
21114
  const tblId = nextTableId();
21115
+ const prof = usableProfile(tp, tblId, rowCnt, colCnt);
21116
+ const colW = resolveColWidths(prof, colCnt, 44e3);
21117
+ const tblW = colW.reduce((a, b) => a + b, 0);
21118
+ const tblH = cellH * rowCnt;
20949
21119
  const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
20950
21120
  const trElements = rows.map((row, rowIdx) => {
20951
21121
  const cells = row.length < colCnt ? [...row, ...Array(colCnt - row.length).fill("")] : row;
20952
21122
  const isHeaderRow = rowIdx === 0;
20953
21123
  const headerCharPr = isHeaderRow && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
20954
21124
  const tdElements = cells.map((cell, colIdx) => {
20955
- const runs = generateRuns(cell, headerCharPr);
21125
+ const k = `${rowIdx},${colIdx}`;
21126
+ const bf = prof?.cellBf.get(k) ?? 2;
21127
+ const ch = prof?.cellChar.get(k) ?? headerCharPr;
21128
+ const h = prof?.cellH.get(k) ?? cellH;
21129
+ const runs = generateRuns(cell, ch);
20956
21130
  const p = `<hp:p paraPrIDRef="0" styleIDRef="0">${runs}</hp:p>`;
20957
- 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>`;
21131
+ 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>`;
20958
21132
  }).join("");
20959
21133
  return `<hp:tr>${tdElements}</hp:tr>`;
20960
21134
  }).join("");
@@ -20985,35 +21159,42 @@ function layoutHtmlRows(rows) {
20985
21159
  function unescapeHtml(s) {
20986
21160
  return s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&amp;/g, "&");
20987
21161
  }
20988
- function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3) {
21162
+ function generateHtmlTableXml(rawHtml, theme, totalWidth = 44e3, tp = null) {
20989
21163
  const rows = parseHtmlTable(rawHtml);
20990
21164
  if (!rows || rows.length === 0) return null;
20991
21165
  const { placed, rowCnt, colCnt } = layoutHtmlRows(rows);
20992
21166
  if (rowCnt === 0 || colCnt === 0) return null;
20993
- const colW = Math.floor(totalWidth / colCnt);
20994
21167
  const cellH = 1500;
20995
- const tblW = colW * colCnt;
20996
21168
  const tblId = nextTableId();
21169
+ const prof = usableProfile(tp, tblId, rowCnt, colCnt);
21170
+ const colW = resolveColWidths(prof, colCnt, totalWidth);
21171
+ const tblW = colW.reduce((a, b) => a + b, 0);
20997
21172
  const useHeaderStyle = theme.tableHeader !== theme.body || theme.tableHeaderBold;
21173
+ const spanW = (c, colSpan) => colW.slice(c, c + colSpan).reduce((a, b) => a + b, 0);
20998
21174
  const tcXmls = placed.map((cell) => {
21175
+ const k = `${cell.r},${cell.c}`;
21176
+ const bf = prof?.cellBf.get(k) ?? 2;
20999
21177
  const headerCharPr = cell.isHeader && useHeaderStyle ? CHAR_TABLE_HEADER : CHAR_NORMAL;
21178
+ const ch = prof?.cellChar.get(k) ?? headerCharPr;
21000
21179
  const { lines } = htmlCellInnerToLines(cell.inner);
21001
21180
  const paras = lines.map(
21002
- (line) => `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t>${escapeXml(unescapeHtml(line))}</hp:t></hp:run></hp:p>`
21181
+ (line) => `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${ch}"><hp:t>${escapeXml(unescapeHtml(line))}</hp:t></hp:run></hp:p>`
21003
21182
  );
21183
+ const cellW = spanW(cell.c, cell.colSpan);
21004
21184
  let nestedH = 0;
21005
21185
  for (const nested of extractTopLevelTables(cell.inner)) {
21006
- const nestedXml = generateHtmlTableXml(nested, theme, Math.max(colW * cell.colSpan - 1020, 4e3));
21186
+ const nestedXml = generateHtmlTableXml(nested, theme, Math.max(cellW - 1020, 4e3));
21007
21187
  if (nestedXml) {
21008
21188
  paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${nestedXml}</hp:run></hp:p>`);
21009
21189
  nestedH += (nested.match(/<tr[\s>]/gi) ?? []).length * cellH + 300;
21010
21190
  }
21011
21191
  }
21012
21192
  if (paras.length === 0) {
21013
- paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${headerCharPr}"><hp:t></hp:t></hp:run></hp:p>`);
21193
+ paras.push(`<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="${ch}"><hp:t></hp:t></hp:run></hp:p>`);
21014
21194
  }
21015
- const cellHeight = Math.max(cellH * cell.rowSpan, Math.max(lines.length, 1) * 800 + nestedH);
21016
- 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>`;
21195
+ const contentH = Math.max(cellH * cell.rowSpan, Math.max(lines.length, 1) * 800 + nestedH);
21196
+ const cellHeight = Math.max(prof?.cellH.get(k) ?? 0, contentH);
21197
+ 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>`;
21017
21198
  });
21018
21199
  const trXmls = [];
21019
21200
  for (let r = 0; r < rowCnt; r++) {
@@ -21179,9 +21360,9 @@ function readCommand(input, idx, depth) {
21179
21360
  const command = name.value;
21180
21361
  if (command === "\\") return { value: "#", next: name.next };
21181
21362
  if (command === "frac") {
21182
- const num = readGroupOrToken(input, name.next, depth);
21183
- const den = readGroupOrToken(input, num.next, depth);
21184
- return { value: `{${num.value}} over {${den.value}}`, next: den.next };
21363
+ const num2 = readGroupOrToken(input, name.next, depth);
21364
+ const den = readGroupOrToken(input, num2.next, depth);
21365
+ return { value: `{${num2.value}} over {${den.value}}`, next: den.next };
21185
21366
  }
21186
21367
  if (command === "sqrt") {
21187
21368
  let cursor = skipSpaces(input, name.next);
@@ -21312,9 +21493,10 @@ function generateSecPr(gongmun) {
21312
21493
  } : { top: 8504, bottom: 4252, left: 5670, right: 4252, header: 2835, footer: 2835 };
21313
21494
  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>`;
21314
21495
  }
21315
- function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null) {
21496
+ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null, fit = null, chartParts = null, remap = null) {
21316
21497
  const paraXmls = [];
21317
21498
  let isFirst = true;
21499
+ let tableSeq = 0;
21318
21500
  const orderedCounters = {};
21319
21501
  let prevWasOrdered = false;
21320
21502
  for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
@@ -21422,11 +21604,12 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
21422
21604
  paraXmls.push(`<hp:p paraPrIDRef="0" styleIDRef="0">${secRun}</hp:p>`);
21423
21605
  isFirst = false;
21424
21606
  }
21425
- xml = generateTable(block.rows, theme);
21607
+ xml = generateTable(block.rows, theme, remap?.tables.get(tableSeq) ?? null);
21608
+ tableSeq++;
21426
21609
  }
21427
21610
  break;
21428
21611
  case "html_table": {
21429
- const tbl = generateHtmlTableXml(block.text || "", theme);
21612
+ const tbl = generateHtmlTableXml(block.text || "", theme, 44e3, remap?.tables.get(tableSeq) ?? null);
21430
21613
  if (tbl) {
21431
21614
  if (isFirst) {
21432
21615
  const secRun = `<hp:run charPrIDRef="0">${generateSecPr(gongmun)}<hp:t></hp:t></hp:run>`;
@@ -21434,6 +21617,7 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
21434
21617
  isFirst = false;
21435
21618
  }
21436
21619
  xml = `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
21620
+ tableSeq++;
21437
21621
  } else {
21438
21622
  const plain = (block.text || "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
21439
21623
  xml = plain ? generateParagraph(plain) : "";
@@ -21460,6 +21644,93 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
21460
21644
  </hs:sec>`;
21461
21645
  }
21462
21646
 
21647
+ // src/hwpx/gen-profile.ts
21648
+ function parseHu(s) {
21649
+ if (s == null) return void 0;
21650
+ const n = parseInt(String(s).trim(), 10);
21651
+ return Number.isFinite(n) ? n : void 0;
21652
+ }
21653
+ function edgeXml(tag, d) {
21654
+ return d ? `<hh:${tag} type="${d.type}" width="${d.width}" color="${d.color}"/>` : `<hh:${tag} type="NONE" width="0.1 mm" color="#000000"/>`;
21655
+ }
21656
+ function borderFillDefToXml(id, def) {
21657
+ const fill = def.fill?.faceColor ? `<hh:fillBrush><hh:winBrush faceColor="${def.fill.faceColor}" hatchColor="#000000" alpha="0"/></hh:fillBrush>` : "";
21658
+ return ` <hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="NONE" breakCellSeparateLine="0">
21659
+ <hh:slash type="NONE" Crooked="0" isCounter="0"/>
21660
+ <hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
21661
+ ${edgeXml("leftBorder", def.leftBorder)}
21662
+ ${edgeXml("rightBorder", def.rightBorder)}
21663
+ ${edgeXml("topBorder", def.topBorder)}
21664
+ ${edgeXml("bottomBorder", def.bottomBorder)}${fill ? `
21665
+ ${fill}` : ""}
21666
+ </hh:borderFill>`;
21667
+ }
21668
+ function profileCharPrXml(id, def) {
21669
+ const height = parseHu(def.height_hwpunit) ?? 1e3;
21670
+ const color = def.textColor ?? "#000000";
21671
+ const font = def.fontRef_hangul != null ? parseInt(def.fontRef_hangul, 10) || 0 : 0;
21672
+ const boldAttr = def.bold ? ` bold="1"` : "";
21673
+ const italicAttr = def.italic ? ` italic="1"` : "";
21674
+ const underline = def.underline ? `
21675
+ <hh:underline type="BOTTOM" shape="SOLID" color="${color}"/>` : "";
21676
+ return ` <hh:charPr id="${id}" height="${height}" textColor="${color}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
21677
+ <hh:fontRef hangul="${font}" latin="${font}" hanja="${font}" japanese="${font}" other="${font}" symbol="${font}" user="${font}"/>
21678
+ <hh:ratio hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
21679
+ <hh:spacing hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>
21680
+ <hh:relSz hangul="100" latin="100" hanja="100" japanese="100" other="100" symbol="100" user="100"/>
21681
+ <hh:offset hangul="0" latin="0" hanja="0" japanese="0" other="0" symbol="0" user="0"/>${underline}
21682
+ </hh:charPr>`;
21683
+ }
21684
+ function buildProfileRemap(profile, charPrBase, borderFillBase = 3) {
21685
+ const remap = { borderFillXmls: [], charPrXmls: [], tables: /* @__PURE__ */ new Map() };
21686
+ let bfNext = borderFillBase;
21687
+ let charNext = charPrBase;
21688
+ for (const t of profile.tables) {
21689
+ const localBf = {};
21690
+ for (const [key, def] of Object.entries(t.used_border_fills ?? {})) {
21691
+ const gid = bfNext++;
21692
+ remap.borderFillXmls.push(borderFillDefToXml(gid, def));
21693
+ localBf[key] = gid;
21694
+ }
21695
+ const localChar = {};
21696
+ for (const [key, def] of Object.entries(t.used_char_prs ?? {})) {
21697
+ const gid = charNext++;
21698
+ remap.charPrXmls.push(profileCharPrXml(gid, def));
21699
+ localChar[key] = gid;
21700
+ }
21701
+ let colWidths;
21702
+ if (t.col_widths_hwpunit && t.col_widths_hwpunit.length === t.cols) {
21703
+ const parsed = t.col_widths_hwpunit.map(parseHu);
21704
+ if (parsed.every((n) => n != null)) colWidths = parsed;
21705
+ }
21706
+ const tr = {
21707
+ rows: t.rows,
21708
+ cols: t.cols,
21709
+ width: parseHu(t.width_hwpunit),
21710
+ colWidths,
21711
+ cellBf: /* @__PURE__ */ new Map(),
21712
+ cellChar: /* @__PURE__ */ new Map(),
21713
+ cellH: /* @__PURE__ */ new Map()
21714
+ };
21715
+ for (const cell of t.cells) {
21716
+ const k = `${cell.row},${cell.col}`;
21717
+ if (cell.borderFillIDRef != null && cell.borderFillIDRef in localBf) {
21718
+ tr.cellBf.set(k, localBf[cell.borderFillIDRef]);
21719
+ }
21720
+ if (cell.charPrIDRef != null && cell.charPrIDRef in localChar) {
21721
+ tr.cellChar.set(k, localChar[cell.charPrIDRef]);
21722
+ }
21723
+ const h = parseHu(cell.height_hwpunit);
21724
+ if (h != null) tr.cellH.set(k, h);
21725
+ }
21726
+ remap.tables.set(t.table_index, tr);
21727
+ }
21728
+ return remap;
21729
+ }
21730
+ function profileCharPrBase(ratioVariantCount) {
21731
+ return CHAR_VARIANT_BASE + ratioVariantCount * 4;
21732
+ }
21733
+
21463
21734
  // src/hwpx/generator.ts
21464
21735
  async function markdownToHwpx(markdown, options) {
21465
21736
  const theme = resolveTheme(options?.theme);
@@ -21467,13 +21738,14 @@ async function markdownToHwpx(markdown, options) {
21467
21738
  const blocks = parseMarkdownToBlocks(markdown);
21468
21739
  const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
21469
21740
  const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
21741
+ const remap = options?.profile ? buildProfileRemap(options.profile, profileCharPrBase(fit?.variants?.length ?? 0)) : null;
21470
21742
  const chartParts = [];
21471
- const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts);
21743
+ const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, remap);
21472
21744
  const zip = new JSZip6();
21473
21745
  zip.file("mimetype", "application/hwp+zip", { compression: "STORE" });
21474
21746
  zip.file("META-INF/container.xml", generateContainerXml());
21475
21747
  zip.file("Contents/content.hpf", generateManifest(chartParts));
21476
- zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? []));
21748
+ zip.file("Contents/header.xml", generateHeaderXml(theme, gongmun, fit?.variants ?? [], remap));
21477
21749
  zip.file("Contents/section0.xml", sectionXml);
21478
21750
  for (const part of chartParts) zip.file(part.name, part.xml);
21479
21751
  zip.file("Preview/PrvText.txt", buildPrvText(blocks));
@@ -21614,8 +21886,186 @@ function diffTableCells(a, b) {
21614
21886
  return result;
21615
21887
  }
21616
21888
 
21617
- // src/roundtrip/patcher.ts
21889
+ // src/hwpx/extract-profile.ts
21618
21890
  import JSZip7 from "jszip";
21891
+ function elemsByLocal(root, name) {
21892
+ const all = root.getElementsByTagName("*");
21893
+ const out = [];
21894
+ for (let i = 0; i < all.length; i++) {
21895
+ const el = all[i];
21896
+ const tag = (el.tagName || el.localName || "").replace(/^[^:]+:/, "");
21897
+ if (tag === name) out.push(el);
21898
+ }
21899
+ return out;
21900
+ }
21901
+ function borderDefOf(el) {
21902
+ if (!el) return void 0;
21903
+ const type = el.getAttribute("type") || "NONE";
21904
+ const width = el.getAttribute("width") || "0.1 mm";
21905
+ const color = el.getAttribute("color") || "#000000";
21906
+ return { type, width, color };
21907
+ }
21908
+ function parseBorderFills(headerDoc) {
21909
+ const map = /* @__PURE__ */ new Map();
21910
+ for (const bf of elemsByLocal(headerDoc, "borderFill")) {
21911
+ const id = bf.getAttribute("id");
21912
+ if (!id) continue;
21913
+ const def = {
21914
+ leftBorder: borderDefOf(findChildByLocalName(bf, "leftBorder")),
21915
+ rightBorder: borderDefOf(findChildByLocalName(bf, "rightBorder")),
21916
+ topBorder: borderDefOf(findChildByLocalName(bf, "topBorder")),
21917
+ bottomBorder: borderDefOf(findChildByLocalName(bf, "bottomBorder"))
21918
+ };
21919
+ const fillBrush = findChildByLocalName(bf, "fillBrush");
21920
+ const winBrush = fillBrush ? findChildByLocalName(fillBrush, "winBrush") : null;
21921
+ const face = winBrush?.getAttribute("faceColor");
21922
+ if (face && face !== "none") def.fill = { faceColor: face };
21923
+ map.set(id, def);
21924
+ }
21925
+ return map;
21926
+ }
21927
+ function parseCharPrs(headerDoc) {
21928
+ const map = /* @__PURE__ */ new Map();
21929
+ for (const cp of elemsByLocal(headerDoc, "charPr")) {
21930
+ const id = cp.getAttribute("id");
21931
+ if (!id) continue;
21932
+ const def = {};
21933
+ const h = cp.getAttribute("height");
21934
+ if (h) def.height_hwpunit = h;
21935
+ const color = cp.getAttribute("textColor");
21936
+ if (color) def.textColor = color;
21937
+ if (cp.getAttribute("bold") === "1") def.bold = true;
21938
+ if (cp.getAttribute("italic") === "1") def.italic = true;
21939
+ if (findChildByLocalName(cp, "underline")) def.underline = true;
21940
+ const fontRef = findChildByLocalName(cp, "fontRef");
21941
+ const hangul = fontRef?.getAttribute("hangul");
21942
+ if (hangul) def.fontRef_hangul = hangul;
21943
+ map.set(id, def);
21944
+ }
21945
+ return map;
21946
+ }
21947
+ function isTopLevelTable(tbl) {
21948
+ let p = tbl.parentNode;
21949
+ while (p) {
21950
+ const tag = (p.tagName || p.localName || "").replace(/^[^:]+:/, "");
21951
+ if (tag === "tbl") return false;
21952
+ p = p.parentNode;
21953
+ }
21954
+ return true;
21955
+ }
21956
+ function num(s) {
21957
+ if (s == null) return void 0;
21958
+ const n = parseInt(s, 10);
21959
+ return Number.isFinite(n) ? n : void 0;
21960
+ }
21961
+ function parseTable3(tbl, tableIndex, borderFills, charPrs) {
21962
+ const rows = num(tbl.getAttribute("rowCnt")) ?? 0;
21963
+ const cols = num(tbl.getAttribute("colCnt")) ?? 0;
21964
+ const sz = findChildByLocalName(tbl, "sz");
21965
+ const width = sz?.getAttribute("width") || void 0;
21966
+ const cells = [];
21967
+ const usedBf = /* @__PURE__ */ new Set();
21968
+ const usedCp = /* @__PURE__ */ new Set();
21969
+ const colWidths = new Array(cols).fill(void 0);
21970
+ for (const tc of elemsByLocal(tbl, "tc")) {
21971
+ if (nearestTable(tc) !== tbl) continue;
21972
+ const addr = findChildByLocalName(tc, "cellAddr");
21973
+ const span = findChildByLocalName(tc, "cellSpan");
21974
+ const csz = findChildByLocalName(tc, "cellSz");
21975
+ const row = num(addr?.getAttribute("rowAddr")) ?? 0;
21976
+ const col = num(addr?.getAttribute("colAddr")) ?? 0;
21977
+ const colSpan = num(span?.getAttribute("colSpan")) ?? 1;
21978
+ const rowSpan = num(span?.getAttribute("rowSpan")) ?? 1;
21979
+ const bfId = tc.getAttribute("borderFillIDRef") || void 0;
21980
+ const cpId = firstRunCharPr(tc);
21981
+ const cell = { row, col, rowSpan, colSpan };
21982
+ const w = csz?.getAttribute("width");
21983
+ const hh = csz?.getAttribute("height");
21984
+ if (w) cell.width_hwpunit = w;
21985
+ if (hh) cell.height_hwpunit = hh;
21986
+ if (bfId) {
21987
+ cell.borderFillIDRef = bfId;
21988
+ usedBf.add(bfId);
21989
+ }
21990
+ if (cpId) {
21991
+ cell.charPrIDRef = cpId;
21992
+ usedCp.add(cpId);
21993
+ }
21994
+ cells.push(cell);
21995
+ if (row === 0 && colSpan === 1 && col < cols && w) colWidths[col] = w;
21996
+ }
21997
+ const table = {
21998
+ table_index: tableIndex,
21999
+ rows,
22000
+ cols,
22001
+ cells,
22002
+ used_border_fills: pick(borderFills, usedBf)
22003
+ };
22004
+ if (width) table.width_hwpunit = width;
22005
+ if (colWidths.every((w) => w != null)) table.col_widths_hwpunit = colWidths;
22006
+ const cp = pick(charPrs, usedCp);
22007
+ if (Object.keys(cp).length) table.used_char_prs = cp;
22008
+ return table;
22009
+ }
22010
+ function nearestTable(tc) {
22011
+ let p = tc.parentNode;
22012
+ while (p) {
22013
+ const tag = (p.tagName || p.localName || "").replace(/^[^:]+:/, "");
22014
+ if (tag === "tbl") return p;
22015
+ p = p.parentNode;
22016
+ }
22017
+ return null;
22018
+ }
22019
+ function firstRunCharPr(tc) {
22020
+ for (const run of elemsByLocal(tc, "run")) {
22021
+ if (nearestCell(run) !== tc) continue;
22022
+ const id = run.getAttribute("charPrIDRef");
22023
+ if (id) return id;
22024
+ }
22025
+ return void 0;
22026
+ }
22027
+ function nearestCell(el) {
22028
+ let p = el.parentNode;
22029
+ while (p) {
22030
+ const tag = (p.tagName || p.localName || "").replace(/^[^:]+:/, "");
22031
+ if (tag === "tc") return p;
22032
+ p = p.parentNode;
22033
+ }
22034
+ return null;
22035
+ }
22036
+ function pick(map, keys) {
22037
+ const out = {};
22038
+ for (const k of keys) {
22039
+ const v = map.get(k);
22040
+ if (v !== void 0) out[k] = v;
22041
+ }
22042
+ return out;
22043
+ }
22044
+ async function hwpxToProfile(input) {
22045
+ const buf = input instanceof ArrayBuffer ? input : toArrayBuffer(input);
22046
+ const zip = await JSZip7.loadAsync(buf);
22047
+ const parser = createXmlParser();
22048
+ const headerFile = zip.file("Contents/header.xml") ?? zip.file(/[Hh]eader\.xml$/)?.[0];
22049
+ let headerXml = "<root/>";
22050
+ if (headerFile) headerXml = await headerFile.async("text");
22051
+ const headerDoc = parser.parseFromString(headerXml, "text/xml");
22052
+ const borderFills = parseBorderFills(headerDoc);
22053
+ const charPrs = parseCharPrs(headerDoc);
22054
+ 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));
22055
+ const tables = [];
22056
+ let tableIndex = 0;
22057
+ for (const f of sectionFiles) {
22058
+ const doc = parser.parseFromString(await f.async("text"), "text/xml");
22059
+ for (const tbl of elemsByLocal(doc, "tbl")) {
22060
+ if (!isTopLevelTable(tbl)) continue;
22061
+ tables.push(parseTable3(tbl, tableIndex++, borderFills, charPrs));
22062
+ }
22063
+ }
22064
+ return { schema_version: "0.1.0", tables };
22065
+ }
22066
+
22067
+ // src/roundtrip/patcher.ts
22068
+ import JSZip8 from "jszip";
21619
22069
 
21620
22070
  // src/roundtrip/table-rows.ts
21621
22071
  var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
@@ -22253,7 +22703,7 @@ async function patchHwpx(original, editedMarkdown, options) {
22253
22703
  }
22254
22704
  let zip;
22255
22705
  try {
22256
- zip = await JSZip7.loadAsync(original);
22706
+ zip = await JSZip8.loadAsync(original);
22257
22707
  } catch {
22258
22708
  return { success: false, applied: 0, skipped, error: "ZIP \uB85C\uB4DC \uC2E4\uD328" };
22259
22709
  }
@@ -22344,16 +22794,16 @@ function buildOrigUnits(blocks) {
22344
22794
  for (let i = 0; i < blocks.length; i++) {
22345
22795
  const block = blocks[i];
22346
22796
  let consume = 1;
22347
- let chunk;
22797
+ let chunk2;
22348
22798
  if (block.type === "paragraph" && block.text && /^\[별표\s*\d+/.test(sanitizeText(block.text))) {
22349
22799
  const next = blocks[i + 1];
22350
22800
  if (next?.type === "paragraph" && next.text && /관련\)?$/.test(next.text)) consume = 2;
22351
- chunk = blocksToMarkdown(blocks.slice(i, i + consume));
22801
+ chunk2 = blocksToMarkdown(blocks.slice(i, i + consume));
22352
22802
  } else {
22353
- chunk = blocksToMarkdown([block]);
22803
+ chunk2 = blocksToMarkdown([block]);
22354
22804
  }
22355
- if (chunk) {
22356
- const subUnits = splitMarkdownUnits(chunk);
22805
+ if (chunk2) {
22806
+ const subUnits = splitMarkdownUnits(chunk2);
22357
22807
  const isFragment = consume === 2 || (block.type === "paragraph" || block.type === "heading") && subUnits.length > 1;
22358
22808
  for (let s = 0; s < subUnits.length; s++) {
22359
22809
  const u = { ...subUnits[s], blockIdx: i, fragment: isFragment || void 0 };
@@ -23187,19 +23637,19 @@ function resolveTableMappings(blocks, scanTables) {
23187
23637
  if (scanTables[k].rows === table.rows && scanTables[k].cols === table.cols) cands.push(k);
23188
23638
  }
23189
23639
  if (cands.length === 0) continue;
23190
- let pick = cands[0];
23640
+ let pick2 = cands[0];
23191
23641
  if (cands.length > 1) {
23192
23642
  let best = tableContentScore(table, scanTables[cands[0]]);
23193
23643
  for (let ci = 1; ci < cands.length; ci++) {
23194
23644
  const sc = tableContentScore(table, scanTables[cands[ci]]);
23195
23645
  if (sc > best) {
23196
23646
  best = sc;
23197
- pick = cands[ci];
23647
+ pick2 = cands[ci];
23198
23648
  }
23199
23649
  }
23200
23650
  }
23201
- result.set(i, scanTables[pick]);
23202
- si = pick + 1;
23651
+ result.set(i, scanTables[pick2]);
23652
+ si = pick2 + 1;
23203
23653
  }
23204
23654
  return result;
23205
23655
  }
@@ -23675,7 +24125,7 @@ function stageParaPatch(scan, para, newPlain, skip) {
23675
24125
  }
23676
24126
 
23677
24127
  // src/validate.ts
23678
- import JSZip8 from "jszip";
24128
+ import JSZip9 from "jszip";
23679
24129
  import { DOMParser as DOMParser4 } from "@xmldom/xmldom";
23680
24130
  var REQUIRED_FILES = [
23681
24131
  "mimetype",
@@ -23693,7 +24143,7 @@ async function validateHwpx(buffer) {
23693
24143
  const issues = [];
23694
24144
  let zip;
23695
24145
  try {
23696
- zip = await JSZip8.loadAsync(buffer);
24146
+ zip = await JSZip9.loadAsync(buffer);
23697
24147
  } catch (err) {
23698
24148
  return {
23699
24149
  ok: false,
@@ -23761,10 +24211,10 @@ async function validateHwpx(buffer) {
23761
24211
  }
23762
24212
 
23763
24213
  // src/roundtrip/session.ts
23764
- import JSZip9 from "jszip";
24214
+ import JSZip10 from "jszip";
23765
24215
  async function buildState(bytes) {
23766
24216
  const parsed = await parseHwpxDocument(u8ToArrayBuffer2(bytes));
23767
- const zip = await JSZip9.loadAsync(bytes);
24217
+ const zip = await JSZip10.loadAsync(bytes);
23768
24218
  const sectionPaths = await resolveSectionEntryNames(zip);
23769
24219
  if (sectionPaths.length === 0) {
23770
24220
  throw new Error("HWPX \uC139\uC158 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
@@ -24345,7 +24795,7 @@ async function parseHwp(buffer, options) {
24345
24795
  async function parsePdf(buffer, options) {
24346
24796
  let parsePdfDocument;
24347
24797
  try {
24348
- const mod = await import("./parser-CYJMOXUR.js");
24798
+ const mod = await import("./parser-NZAE7PBE.js");
24349
24799
  parsePdfDocument = mod.parsePdfDocument;
24350
24800
  } catch {
24351
24801
  return {
@@ -24437,6 +24887,7 @@ async function fillForm(input, values, outputFormat = "markdown") {
24437
24887
 
24438
24888
  export {
24439
24889
  extractHwpxMetadataOnly,
24890
+ MAX_INLINE_MD_BYTES,
24440
24891
  extractHwp5MetadataOnly,
24441
24892
  formatFillValue,
24442
24893
  ValueCursor,
@@ -24452,6 +24903,7 @@ export {
24452
24903
  markdownToHwpx,
24453
24904
  compare,
24454
24905
  diffBlocks,
24906
+ hwpxToProfile,
24455
24907
  patchHwpx,
24456
24908
  patchHwp,
24457
24909
  validateHwpx,
@@ -24472,4 +24924,4 @@ export {
24472
24924
  parseHwpml,
24473
24925
  fillForm
24474
24926
  };
24475
- //# sourceMappingURL=chunk-VUS6LRVY.js.map
24927
+ //# sourceMappingURL=chunk-PWHM3KAA.js.map