kordoc 4.0.3 → 4.0.4

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 (59) hide show
  1. package/README.md +8 -0
  2. package/dist/{-QSF64HNK.js → -Q4ZCVQFI.js} +9 -7
  3. package/dist/chunk-2RIOXYGA.js +92 -0
  4. package/dist/chunk-2RIOXYGA.js.map +1 -0
  5. package/dist/chunk-5RPYBC2Q.js +88 -0
  6. package/dist/chunk-5RPYBC2Q.js.map +1 -0
  7. package/dist/{chunk-AUJICYJG.js → chunk-DKMKJMR6.js} +4 -3
  8. package/dist/chunk-DKMKJMR6.js.map +1 -0
  9. package/dist/{chunk-6JSZBSOD.js → chunk-KA5AN73R.js} +2 -2
  10. package/dist/{chunk-TQYX5E32.js → chunk-KSVLQ5BC.js} +33 -4
  11. package/dist/chunk-KSVLQ5BC.js.map +1 -0
  12. package/dist/{chunk-K2SETHJ6.js → chunk-MLNHZN32.js} +1589 -765
  13. package/dist/chunk-MLNHZN32.js.map +1 -0
  14. package/dist/{chunk-OVESZY5R.cjs → chunk-NIW7CZYX.cjs} +37 -8
  15. package/dist/chunk-NIW7CZYX.cjs.map +1 -0
  16. package/dist/{chunk-MQLKSRIK.js → chunk-S3KGEEBQ.js} +76 -25
  17. package/dist/chunk-S3KGEEBQ.js.map +1 -0
  18. package/dist/{chunk-RO2GVZ3I.js → chunk-TXNRIVWA.js} +3 -3
  19. package/dist/{chunk-PNQDQ3SM.js → chunk-ZJXR4PWY.js} +33 -4
  20. package/dist/chunk-ZJXR4PWY.js.map +1 -0
  21. package/dist/cli.js +119 -72
  22. package/dist/cli.js.map +1 -1
  23. package/dist/index.cjs +21122 -20246
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +47 -1
  26. package/dist/index.d.ts +47 -1
  27. package/dist/index.js +20944 -20068
  28. package/dist/index.js.map +1 -1
  29. package/dist/mcp.js +106 -83
  30. package/dist/mcp.js.map +1 -1
  31. package/dist/{parser-FMONFTAN.cjs → parser-2ZUTCU2G.cjs} +106 -17
  32. package/dist/parser-2ZUTCU2G.cjs.map +1 -0
  33. package/dist/{parser-AODAQQLQ.js → parser-46JGSTPR.js} +95 -6
  34. package/dist/parser-46JGSTPR.js.map +1 -0
  35. package/dist/{parser-J7YREDNS.js → parser-TLCC67W6.js} +94 -5
  36. package/dist/parser-TLCC67W6.js.map +1 -0
  37. package/dist/profile-io-MRVIJ5T4.js +11 -0
  38. package/dist/render-TTOG6CHW.js +10 -0
  39. package/dist/seal-NMLZ3M3K.js +10 -0
  40. package/dist/seal-NMLZ3M3K.js.map +1 -0
  41. package/dist/{watch-P65Z24AL.js → watch-NZNVJ4YD.js} +7 -7
  42. package/package.json +1 -1
  43. package/dist/chunk-AUJICYJG.js.map +0 -1
  44. package/dist/chunk-K2SETHJ6.js.map +0 -1
  45. package/dist/chunk-MQLKSRIK.js.map +0 -1
  46. package/dist/chunk-OVESZY5R.cjs.map +0 -1
  47. package/dist/chunk-PNQDQ3SM.js.map +0 -1
  48. package/dist/chunk-TQYX5E32.js.map +0 -1
  49. package/dist/parser-AODAQQLQ.js.map +0 -1
  50. package/dist/parser-FMONFTAN.cjs.map +0 -1
  51. package/dist/parser-J7YREDNS.js.map +0 -1
  52. package/dist/render-JPP7YTV3.js +0 -10
  53. package/dist/seal-MDE4DXZQ.js +0 -10
  54. /package/dist/{-QSF64HNK.js.map → -Q4ZCVQFI.js.map} +0 -0
  55. /package/dist/{chunk-6JSZBSOD.js.map → chunk-KA5AN73R.js.map} +0 -0
  56. /package/dist/{chunk-RO2GVZ3I.js.map → chunk-TXNRIVWA.js.map} +0 -0
  57. /package/dist/{render-JPP7YTV3.js.map → profile-io-MRVIJ5T4.js.map} +0 -0
  58. /package/dist/{seal-MDE4DXZQ.js.map → render-TTOG6CHW.js.map} +0 -0
  59. /package/dist/{watch-P65Z24AL.js.map → watch-NZNVJ4YD.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,31 +1,42 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ parseFormatProfileJson
4
+ } from "./chunk-2RIOXYGA.js";
5
+ import {
6
+ BODY_FONTS,
7
+ BULLET2_CHARS,
8
+ H2_MARKERS,
9
+ buildGongmunOptions
10
+ } from "./chunk-5RPYBC2Q.js";
2
11
  import {
3
12
  PRESET_ALIAS,
4
13
  extractFormFields,
5
14
  fillFormFields,
6
15
  fillHwpx,
7
16
  gongmunLintWarnings,
17
+ hwpxToProfile,
18
+ incompatibleGongmunWarnings,
8
19
  lintGongmunText,
9
20
  markdownToHwpx,
10
21
  parse,
11
22
  unknownFontWarnings
12
- } from "./chunk-K2SETHJ6.js";
23
+ } from "./chunk-MLNHZN32.js";
13
24
  import {
14
25
  blocksToMarkdown
15
- } from "./chunk-PNQDQ3SM.js";
26
+ } from "./chunk-ZJXR4PWY.js";
16
27
  import {
17
28
  detectFormat,
18
29
  detectZipFormat
19
30
  } from "./chunk-MEPHGCPQ.js";
20
31
  import "./chunk-MOL7MDBG.js";
21
- import "./chunk-RO2GVZ3I.js";
22
- import "./chunk-MQLKSRIK.js";
23
- import "./chunk-AUJICYJG.js";
32
+ import "./chunk-TXNRIVWA.js";
33
+ import "./chunk-S3KGEEBQ.js";
34
+ import "./chunk-DKMKJMR6.js";
24
35
  import {
25
36
  VERSION,
26
37
  sanitizeError,
27
38
  toArrayBuffer
28
- } from "./chunk-6JSZBSOD.js";
39
+ } from "./chunk-KA5AN73R.js";
29
40
 
30
41
  // src/cli.ts
31
42
  import { readFileSync, writeFileSync, mkdirSync, statSync } from "fs";
@@ -133,7 +144,7 @@ program.command("watch <dir>").description("\uB514\uB809\uD1A0\uB9AC \uAC10\uC2D
133
144
  if (command.getOptionValueSource("format") === "default" && program.getOptionValueSource("format") === "cli") {
134
145
  opts.format = rootOpts.format;
135
146
  }
136
- const { watchDirectory } = await import("./watch-P65Z24AL.js");
147
+ const { watchDirectory } = await import("./watch-NZNVJ4YD.js");
137
148
  await watchDirectory({
138
149
  dir,
139
150
  outDir: opts.outDir,
@@ -290,7 +301,7 @@ program.command("fill <template>").description("\uC11C\uC2DD \uBB38\uC11C\uC758
290
301
  });
291
302
  program.command("seal <file>").description('\uB3C4\uC7A5/\uC11C\uBA85 \uC774\uBBF8\uC9C0\uB97C \uC575\uCEE4 \uBB38\uAD6C \uC704\uC5D0 \uBD80\uC720 \uBC30\uCE58 (\uD45C/\uD398\uC774\uC9C0 \uBD88\uD655\uC7A5) \u2014 kordoc seal \uC2E0\uCCAD\uC11C.hwpx --image \uB3C4\uC7A5.png --anchor "(\uC778)" -o \uACB0\uACFC.hwpx').requiredOption("--image <path>", "\uB3C4\uC7A5/\uC11C\uBA85 \uC774\uBBF8\uC9C0 (\uD22C\uBA85 \uBC30\uACBD PNG \uAD8C\uC7A5)").option("--anchor <text>", "\uC575\uCEE4 \uBB38\uAD6C", "(\uC778)").option("-n, --occurrence <num>", "\uAC19\uC740 \uC575\uCEE4\uAC00 \uC5EC\uB7FF\uC77C \uB54C 0-based \uC120\uD0DD", "0").option("--size-mm <num>", "\uB3C4\uC7A5 \uD55C \uBCC0 \uD06C\uAE30 mm (\uAE30\uBCF8: \uC904\uB192\uC774\xD71.6, 7~18 \uD074\uB7A8\uD504)").option("--mode <mode>", "overlap(\uBB38\uAD6C \uC704 \uACB9\uCE68) | right(\uC624\uB978\uCABD \uC606) | auto", "auto").option("--dx <mm>", "x \uBBF8\uC138\uC870\uC815 mm", "0").option("--dy <mm>", "y \uBBF8\uC138\uC870\uC815 mm", "0").option("-o, --output <path>", "\uCD9C\uB825 \uACBD\uB85C (\uAE30\uBCF8: <\uC785\uB825>.sealed.hwpx)").option("--silent", "\uC9C4\uD589 \uBA54\uC2DC\uC9C0 \uC228\uAE30\uAE30").action(async (file, opts) => {
292
303
  try {
293
- const { placeSealHwpx, detectFormat: detectFormat2 } = await import("./-QSF64HNK.js");
304
+ const { placeSealHwpx, detectFormat: detectFormat2 } = await import("./-Q4ZCVQFI.js");
294
305
  const rootOpts = program.opts();
295
306
  const output = opts.output ?? rootOpts.output;
296
307
  const silent = opts.silent ?? rootOpts.silent;
@@ -315,6 +326,15 @@ program.command("seal <file>").description('\uB3C4\uC7A5/\uC11C\uBA85 \uC774\uBB
315
326
  }
316
327
  const image = new Uint8Array(readFileSync(imgPath));
317
328
  const ext = extname(opts.image).slice(1).toLowerCase() || "png";
329
+ const parseNum = (flag, raw, { min, allowNeg = false } = {}) => {
330
+ const n = Number(raw);
331
+ if (!Number.isFinite(n) || !allowNeg && n < 0 || min !== void 0 && n < min) {
332
+ process.stderr.write(`[kordoc] ${flag} \uAC12\uC774 \uC798\uBABB\uB410\uC2B5\uB2C8\uB2E4: ${raw}${allowNeg ? "" : " (0 \uC774\uC0C1 \uC22B\uC790)"}
333
+ `);
334
+ process.exit(1);
335
+ }
336
+ return n;
337
+ };
318
338
  let sizeMm;
319
339
  if (opts.sizeMm != null) {
320
340
  const n = Number(opts.sizeMm);
@@ -329,13 +349,13 @@ program.command("seal <file>").description('\uB3C4\uC7A5/\uC11C\uBA85 \uC774\uBB
329
349
  buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength),
330
350
  [{
331
351
  anchor: opts.anchor,
332
- occurrence: Number(opts.occurrence) || 0,
352
+ occurrence: parseNum("--occurrence", opts.occurrence),
333
353
  image,
334
354
  ext,
335
355
  sizeMm,
336
356
  mode,
337
- dxMm: Number(opts.dx) || 0,
338
- dyMm: Number(opts.dy) || 0
357
+ dxMm: parseNum("--dx", opts.dx, { allowNeg: true }),
358
+ dyMm: parseNum("--dy", opts.dy, { allowNeg: true })
339
359
  }]
340
360
  );
341
361
  const outPath = resolve(output ?? file.replace(/\.hwpx$/i, "") + ".sealed.hwpx");
@@ -359,7 +379,7 @@ program.command("seal <file>").description('\uB3C4\uC7A5/\uC11C\uBA85 \uC774\uBB
359
379
  });
360
380
  program.command("patch <original> <edited>").description("\uC11C\uC2DD \uBCF4\uC874 \uB77C\uC6B4\uB4DC\uD2B8\uB9BD \uD328\uCE58 \u2014 \uD3B8\uC9D1\uB41C \uB9C8\uD06C\uB2E4\uC6B4\uC744 \uC6D0\uBCF8 HWPX/HWP\uC5D0 in-place \uBC18\uC601 (kordoc patch \uC6D0\uBCF8.hwpx \uD3B8\uC9D1.md -o \uCD9C\uB825.hwpx)").option("-o, --output <path>", "\uCD9C\uB825 \uACBD\uB85C (\uAE30\uBCF8: <\uC6D0\uBCF8>.patched.hwpx|.hwp)").option("--no-verify", "\uD328\uCE58 \uD6C4 \uC7AC\uD30C\uC2F1 \uC790\uB3D9 \uAC80\uC99D \uC0DD\uB7B5").option("--silent", "\uC9C4\uD589 \uBA54\uC2DC\uC9C0 \uC228\uAE30\uAE30").action(async (original, edited, opts) => {
361
381
  try {
362
- const { patchHwpx, patchHwp, detectFormat: detectFormat2 } = await import("./-QSF64HNK.js");
382
+ const { patchHwpx, patchHwp, detectFormat: detectFormat2 } = await import("./-Q4ZCVQFI.js");
363
383
  const rootOpts = program.opts();
364
384
  const output = opts.output ?? rootOpts.output;
365
385
  const silent = opts.silent ?? rootOpts.silent;
@@ -400,7 +420,7 @@ program.command("patch <original> <edited>").description("\uC11C\uC2DD \uBCF4\uC
400
420
  });
401
421
  program.command("validate <file>").description("HWPX \uAD6C\uC870 \uAC80\uC99D \u2014 ZIP\xB7mimetype\xB7\uD544\uC218 \uD30C\uC77C\xB7XML \uC6F0\uD3FC\uB4DC\xB7secCnt\xB7manifest \uCC38\uC870 (\uD55C\uCEF4\uB3C5\uC2A4 \uAC70\uBD80 \uC694\uC778 \uC0AC\uC804 \uCC28\uB2E8)").option("--json", "\uACB0\uACFC\uB97C JSON\uC73C\uB85C stdout\uC5D0 \uCD9C\uB825").action(async (file, opts) => {
402
422
  try {
403
- const { validateHwpx } = await import("./-QSF64HNK.js");
423
+ const { validateHwpx } = await import("./-Q4ZCVQFI.js");
404
424
  const buf = new Uint8Array(readFileSync(resolve(file)));
405
425
  const result = await validateHwpx(buf);
406
426
  if (opts.json) {
@@ -423,7 +443,7 @@ program.command("validate <file>").description("HWPX \uAD6C\uC870 \uAC80\uC99D \
423
443
  process.exit(1);
424
444
  }
425
445
  });
426
- program.command("generate <markdown>").alias("gen").description("\uB9C8\uD06C\uB2E4\uC6B4 \u2192 \uACF5\uBB38\uC11C HWPX \uC0DD\uC131 \u2014 kordoc generate \uBCF4\uACE0\uC11C.md -o \uBCF4\uACE0\uC11C.hwpx --preset \uBCF4\uACE0\uC11C (markdown\uC5D0 '-' \uC9C0\uC815 \uC2DC stdin)").option("-o, --output <path>", "\uCD9C\uB825 HWPX \uACBD\uB85C (\uAE30\uBCF8: <\uC785\uB825>.hwpx)").option("--preset <name>", "\uACF5\uBB38\uC11C \uD504\uB9AC\uC14B: \uAE30\uC548\uBB38(official)\xB7\uBCF4\uACE0\uC11C(report)\xB7\uACC4\uD68D\uC11C(plan)\xB7\uD1B5\uC9C0(notice)\xB7\uD68C\uC758\uB85D(minutes)\xB7\uAC1C\uC870\uC2DD(gaejosik \u2014 \uD45C\uC9C0\xB7\uBAA9\uCC28\xB7\uC7A5\uD5E4\uB354 \uC790\uB3D9)\xB7\uBCF4\uB3C4\uC790\uB8CC(press)", "\uAE30\uC548\uBB38").option("--font <type>", "\uBCF8\uBB38 \uAE00\uAF34: myeongjo(\uD568\uCD08\uB86C\uBC14\uD0D5) \uB610\uB294 gothic(\uB9D1\uC740 \uACE0\uB515)").option("--pt <size>", "\uBCF8\uBB38 \uAE00\uC790 \uD06C\uAE30(pt)").option("--line-spacing <percent>", "\uBCF8\uBB38 \uC904\uAC04\uACA9(%)").option("--org <name>", "\uD45C\uC9C0 \uAE30\uAD00\uBA85 (\uD45C\uC9C0\uB97C \uCF1C\uB294 \uD504\uB9AC\uC14B \uACF5\uD1B5)").option("--date <date>", "\uD45C\uC9C0 \uB0A0\uC9DC (\uAE30\uBCF8 \uC624\uB298 \u2014 'YYYY. M. D.')").option("--toc", "\uBAA9\uCC28 \uD398\uC774\uC9C0 \uAC15\uC81C \uCF1C\uAE30 (\uAC1C\uC870\uC2DD \uC678 \uD504\uB9AC\uC14B\uC5D0\uC11C\uB3C4 h2 \uBAA9\uB85D\uC73C\uB85C \uC0DD\uC131)").option("--no-toc", "\uBAA9\uCC28 \uD398\uC774\uC9C0 \uB044\uAE30 (\uAC1C\uC870\uC2DD \uD504\uB9AC\uC14B \uAE30\uBCF8 \uCF1C\uC9D0)").option("--cover", "\uD45C\uC9C0 \uD398\uC774\uC9C0 \uAC15\uC81C \uCF1C\uAE30 (\uAC1C\uC870\uC2DD \uC678 \uD504\uB9AC\uC14B\uC5D0\uC11C\uB3C4 \uCCAB h1\uC744 \uD45C\uC9C0\uB85C)").option("--no-cover", "\uD45C\uC9C0 \uD398\uC774\uC9C0 \uB044\uAE30 (\uAC1C\uC870\uC2DD \uD504\uB9AC\uC14B \uAE30\uBCF8 \uCF1C\uC9D0)").option("--approval <labels>", "\uACB0\uC7AC\uB780 \uC9C1\uC704 \uB77C\uBCA8 (\uC27C\uD45C \uAD6C\uBD84, \uC608: \uB2F4\uB2F9,\uD300\uC7A5,\uACFC\uC7A5) \u2014 \uBB38\uC11C \uCD5C\uC0C1\uB2E8 \uC6B0\uCE21").option("--page-numbers", "\uCABD\uBC88\uD638 \uAC15\uC81C \uCF1C\uAE30 (\uD558\uB2E8 \uC911\uC559 '- 1 -')").option("--no-page-numbers", "\uCABD\uBC88\uD638 \uB044\uAE30 (\uAC1C\uC870\uC2DD\xB7\uBCF4\uACE0\uC11C\xB7\uACC4\uD68D\uC11C \uAE30\uBCF8 \uCF1C\uC9D0)").option("--end-mark", "\uBCF8\uBB38 \uB05D '\uB05D.' \uD45C\uC2DC \uAC15\uC81C \uCF1C\uAE30").option("--no-end-mark", "'\uB05D.' \uD45C\uC2DC \uB044\uAE30 (\uAE30\uC548\uBB38 \uAE30\uBCF8 \uCF1C\uC9D0)").option("--no-body-title-box", "\uBCF8\uBB38 \uCCAB \uD398\uC774\uC9C0 \uC81C\uBAA9 \uBC18\uBCF5 \uBC15\uC2A4 \uB044\uAE30 (\uAC1C\uC870\uC2DD+\uD45C\uC9C0 \uAE30\uBCF8 \uCF1C\uC9D0)").option("--h2-marker <type>", "h2 \uC139\uC158 \uC81C\uBAA9 \uB9D0\uBA38\uB9AC: box(\u25A1 \u2014 \uBCF4\uACE0\uC11C\xB7\uACC4\uD68D\uC11C \uAE30\uBCF8)\xB7number(1. \u2014 \uACF5\uACE0\uBB38 \uAD00\uD589)\xB7none").option("--fonts <spec>", "\uC694\uC18C\uBCC4 \uAE00\uAF34 \uC624\uBC84\uB77C\uC774\uB4DC: body=\uB098\uB214\uBA85\uC870,heading=\uB098\uB214\uACE0\uB515,ref=\uD55C\uC591\uC911\uACE0\uB515,table=\uB9D1\uC740 \uACE0\uB515").option("--sizes <spec>", "\uAC1C\uC870\uC2DD \uC694\uC18C\uBCC4 \uD06C\uAE30(pt): dae=16,cham=13,table=12,coverTitle=30 \u2026").option("--bullet2 <char>", "2\uB2E8\uACC4 \uD56D\uBAA9\uBD80\uD638: \u3147(\uC774\uC751 \u2014 \uAE30\uC548\uBB38\xB7\uACF5\uACE0\uBB38 \uC2E4\uCE21 \uC9C0\uBC30) \uB610\uB294 \u25CB(\uC6D0 \u2014 \uBCF4\uACE0\uC11C \uC591\uC2DD)").option("--suppress-single", "\uB2E8\uC77C \uD615\uC81C \uD56D\uBAA9 \uBD80\uD638 \uC0DD\uB7B5 (\uD3B8\uB78C \uADDC\uC815 \u2014 \uAE30\uBCF8\uC740 \uD558\uB098\uC5EC\uB3C4 \uBD80\uD638 \uBD80\uC5EC)").option("--doc-head <spec>", "\uAE30\uC548\uBB38 \uB450\uBB38: org=\uD589\uC815\uC548\uC804\uBD80,to=\uC218\uC2E0\uCC98,title=\uC81C\uBAA9 (\uBCC4\uC9C0 \uC81C1\uD638\uC11C\uC2DD)").option("--doc-foot <spec>", "\uAE30\uC548\uBB38 \uACB0\uBB38: sender=\uC7A5\uAD00\uBA85\uC758,drafter=\uAE30\uC548\uC790,reviewer=\uAC80\uD1A0\uC790,approver=\uACB0\uC7AC\uAD8C\uC790,docNum=\uC2DC\uD589\uBC88\uD638,phone=\uC804\uD654,email=\uBA54\uC77C,disclosure=\uACF5\uAC1C\uAD6C\uBD84 \u2026").option("--report-info <text>", "\uC5C5\uBB34\uBCF4\uACE0 \uC6B0\uC0C1\uB2E8 \uBCF4\uACE0\uC815\uBCF4 \uD589 \u2014 \uC608: '(2026. 7. 11., \uACFC\uC7A5 \uD64D\uAE38\uB3D9, \u260E02-120)'").option("--notice-head <spec>", "\uACF5\uACE0\uBB38 \uB450\uBB38\xB7\uACB0\uBB38: no=\uACF5\uACE0 \uC81C2026-1\uD638,date=2026\uB144 7\uC6D4 11\uC77C,sender=\uD589\uC815\uC548\uC804\uBD80\uC7A5\uAD00").option("--press-head <spec>", "\uBCF4\uB3C4\uC790\uB8CC \uBA38\uB9AC: release=\uBCF4\uB3C4\uC2DC\uC810,distribute=\uBC30\uD3EC\uC77C,dept=\uB2F4\uB2F9\uBD80\uC11C,manager=\uB2F4\uB2F9\uC790,phone=\uC5F0\uB77D\uCC98").option("--press-sub <items>", "\uBCF4\uB3C4\uC790\uB8CC \uBD80\uC81C (\uC138\uBBF8\uCF5C\uB860 \uAD6C\uBD84, \uC81C\uBAA9 \uC544\uB798 '- \u2026 -')").option("--plain", "\uACF5\uBB38\uC11C \uBAA8\uB4DC \uB044\uAE30 (\uBC94\uC6A9 \uB9C8\uD06C\uB2E4\uC6B4 \uBCC0\uD658)").option("--silent", "\uC9C4\uD589 \uBA54\uC2DC\uC9C0 \uC228\uAE30\uAE30").action(async (markdown, opts) => {
446
+ program.command("generate <markdown>").alias("gen").description("\uB9C8\uD06C\uB2E4\uC6B4 \u2192 \uACF5\uBB38\uC11C HWPX \uC0DD\uC131 \u2014 kordoc generate \uBCF4\uACE0\uC11C.md -o \uBCF4\uACE0\uC11C.hwpx --preset \uBCF4\uACE0\uC11C (markdown\uC5D0 '-' \uC9C0\uC815 \uC2DC stdin)").option("-o, --output <path>", "\uCD9C\uB825 HWPX \uACBD\uB85C (\uAE30\uBCF8: <\uC785\uB825>.hwpx)").option("--preset <name>", "\uACF5\uBB38\uC11C \uD504\uB9AC\uC14B: \uAE30\uC548\uBB38(official)\xB7\uBCF4\uACE0\uC11C(report)\xB7\uACC4\uD68D\uC11C(plan)\xB7\uD1B5\uC9C0(notice)\xB7\uD68C\uC758\uB85D(minutes)\xB7\uAC1C\uC870\uC2DD(gaejosik \u2014 \uD45C\uC9C0\xB7\uBAA9\uCC28\xB7\uC7A5\uD5E4\uB354 \uC790\uB3D9)\xB7\uBCF4\uB3C4\uC790\uB8CC(press)", "\uAE30\uC548\uBB38").option("--font <type>", "\uBCF8\uBB38 \uAE00\uAF34: myeongjo(\uD568\uCD08\uB86C\uBC14\uD0D5) \uB610\uB294 gothic(\uB9D1\uC740 \uACE0\uB515)").option("--pt <size>", "\uBCF8\uBB38 \uAE00\uC790 \uD06C\uAE30(pt)").option("--line-spacing <percent>", "\uBCF8\uBB38 \uC904\uAC04\uACA9(%)").option("--profile <path>", "\uC11C\uC2DD \uD504\uB85C\uD544 JSON (kordoc profile\uB85C \uCD94\uCD9C) \u2014 \uCC38\uC870 \uBB38\uC11C\uC758 \uD45C \uD14C\uB450\uB9AC\xB7\uC74C\uC601\xB7\uC5F4\uD3ED\xB7\uC140 \uAE00\uAF34 \uC7AC\uD604").option("--org <name>", "\uD45C\uC9C0 \uAE30\uAD00\uBA85 (\uD45C\uC9C0\uB97C \uCF1C\uB294 \uD504\uB9AC\uC14B \uACF5\uD1B5)").option("--date <date>", "\uD45C\uC9C0 \uB0A0\uC9DC (\uAE30\uBCF8 \uC624\uB298 \u2014 'YYYY. M. D.')").option("--toc", "\uBAA9\uCC28 \uD398\uC774\uC9C0 \uAC15\uC81C \uCF1C\uAE30 (\uAC1C\uC870\uC2DD \uC678 \uD504\uB9AC\uC14B\uC5D0\uC11C\uB3C4 h2 \uBAA9\uB85D\uC73C\uB85C \uC0DD\uC131)").option("--no-toc", "\uBAA9\uCC28 \uD398\uC774\uC9C0 \uB044\uAE30 (\uAC1C\uC870\uC2DD \uD504\uB9AC\uC14B \uAE30\uBCF8 \uCF1C\uC9D0)").option("--cover", "\uD45C\uC9C0 \uD398\uC774\uC9C0 \uAC15\uC81C \uCF1C\uAE30 (\uAC1C\uC870\uC2DD \uC678 \uD504\uB9AC\uC14B\uC5D0\uC11C\uB3C4 \uCCAB h1\uC744 \uD45C\uC9C0\uB85C)").option("--no-cover", "\uD45C\uC9C0 \uD398\uC774\uC9C0 \uB044\uAE30 (\uAC1C\uC870\uC2DD \uD504\uB9AC\uC14B \uAE30\uBCF8 \uCF1C\uC9D0)").option("--approval <labels>", "\uACB0\uC7AC\uB780 \uC9C1\uC704 \uB77C\uBCA8 (\uC27C\uD45C \uAD6C\uBD84, \uC608: \uB2F4\uB2F9,\uD300\uC7A5,\uACFC\uC7A5) \u2014 \uBB38\uC11C \uCD5C\uC0C1\uB2E8 \uC6B0\uCE21").option("--page-numbers", "\uCABD\uBC88\uD638 \uAC15\uC81C \uCF1C\uAE30 (\uD558\uB2E8 \uC911\uC559 '- 1 -')").option("--no-page-numbers", "\uCABD\uBC88\uD638 \uB044\uAE30 (\uAC1C\uC870\uC2DD\xB7\uBCF4\uACE0\uC11C\xB7\uACC4\uD68D\uC11C \uAE30\uBCF8 \uCF1C\uC9D0)").option("--end-mark", "\uBCF8\uBB38 \uB05D '\uB05D.' \uD45C\uC2DC \uAC15\uC81C \uCF1C\uAE30").option("--no-end-mark", "'\uB05D.' \uD45C\uC2DC \uB044\uAE30 (\uAE30\uC548\uBB38 \uAE30\uBCF8 \uCF1C\uC9D0)").option("--no-body-title-box", "\uBCF8\uBB38 \uCCAB \uD398\uC774\uC9C0 \uC81C\uBAA9 \uBC18\uBCF5 \uBC15\uC2A4 \uB044\uAE30 (\uAC1C\uC870\uC2DD+\uD45C\uC9C0 \uAE30\uBCF8 \uCF1C\uC9D0)").option("--h2-marker <type>", "h2 \uC139\uC158 \uC81C\uBAA9 \uB9D0\uBA38\uB9AC: box(\u25A1 \u2014 \uBCF4\uACE0\uC11C\xB7\uACC4\uD68D\uC11C \uAE30\uBCF8)\xB7number(1. \u2014 \uACF5\uACE0\uBB38 \uAD00\uD589)\xB7none").option("--fonts <spec>", "\uC694\uC18C\uBCC4 \uAE00\uAF34 \uC624\uBC84\uB77C\uC774\uB4DC: body=\uB098\uB214\uBA85\uC870,heading=\uB098\uB214\uACE0\uB515,ref=\uD55C\uC591\uC911\uACE0\uB515,table=\uB9D1\uC740 \uACE0\uB515").option("--sizes <spec>", "\uAC1C\uC870\uC2DD \uC694\uC18C\uBCC4 \uD06C\uAE30(pt): dae=16,cham=13,table=12,coverTitle=30 \u2026").option("--bullet2 <char>", "2\uB2E8\uACC4 \uD56D\uBAA9\uBD80\uD638: \u3147(\uC774\uC751 \u2014 \uAE30\uC548\uBB38\xB7\uACF5\uACE0\uBB38 \uC2E4\uCE21 \uC9C0\uBC30) \uB610\uB294 \u25CB(\uC6D0 \u2014 \uBCF4\uACE0\uC11C \uC591\uC2DD)").option("--suppress-single", "\uB2E8\uC77C \uD615\uC81C \uD56D\uBAA9 \uBD80\uD638 \uC0DD\uB7B5 (\uD3B8\uB78C \uADDC\uC815 \u2014 \uAE30\uBCF8\uC740 \uD558\uB098\uC5EC\uB3C4 \uBD80\uD638 \uBD80\uC5EC)").option("--doc-head <spec>", "\uAE30\uC548\uBB38 \uB450\uBB38: org=\uD589\uC815\uC548\uC804\uBD80,to=\uC218\uC2E0\uCC98,title=\uC81C\uBAA9 (\uBCC4\uC9C0 \uC81C1\uD638\uC11C\uC2DD)").option("--doc-foot <spec>", "\uAE30\uC548\uBB38 \uACB0\uBB38: sender=\uC7A5\uAD00\uBA85\uC758,drafter=\uAE30\uC548\uC790,reviewer=\uAC80\uD1A0\uC790,approver=\uACB0\uC7AC\uAD8C\uC790,docNum=\uC2DC\uD589\uBC88\uD638,phone=\uC804\uD654,email=\uBA54\uC77C,disclosure=\uACF5\uAC1C\uAD6C\uBD84 \u2026").option("--report-info <text>", "\uC5C5\uBB34\uBCF4\uACE0 \uC6B0\uC0C1\uB2E8 \uBCF4\uACE0\uC815\uBCF4 \uD589 \u2014 \uC608: '(2026. 7. 11., \uACFC\uC7A5 \uD64D\uAE38\uB3D9, \u260E02-120)'").option("--notice-head <spec>", "\uACF5\uACE0\uBB38 \uB450\uBB38\xB7\uACB0\uBB38: no=\uACF5\uACE0 \uC81C2026-1\uD638,date=2026\uB144 7\uC6D4 11\uC77C,sender=\uD589\uC815\uC548\uC804\uBD80\uC7A5\uAD00").option("--press-head <spec>", "\uBCF4\uB3C4\uC790\uB8CC \uBA38\uB9AC: release=\uBCF4\uB3C4\uC2DC\uC810,distribute=\uBC30\uD3EC\uC77C,dept=\uB2F4\uB2F9\uBD80\uC11C,manager=\uB2F4\uB2F9\uC790,phone=\uC5F0\uB77D\uCC98").option("--press-sub <items>", "\uBCF4\uB3C4\uC790\uB8CC \uBD80\uC81C (\uC138\uBBF8\uCF5C\uB860 \uAD6C\uBD84, \uC81C\uBAA9 \uC544\uB798 '- \u2026 -')").option("--plain", "\uACF5\uBB38\uC11C \uBAA8\uB4DC \uB044\uAE30 (\uBC94\uC6A9 \uB9C8\uD06C\uB2E4\uC6B4 \uBCC0\uD658)").option("--silent", "\uC9C4\uD589 \uBA54\uC2DC\uC9C0 \uC228\uAE30\uAE30").action(async (markdown, opts) => {
427
447
  try {
428
448
  const rootOpts = program.opts();
429
449
  const output = opts.output ?? rootOpts.output;
@@ -445,76 +465,85 @@ program.command("generate <markdown>").alias("gen").description("\uB9C8\uD06C\uB
445
465
  `);
446
466
  process.exit(1);
447
467
  }
448
- gongmun = { preset };
449
- if (opts.cover === false) {
450
- gongmun.cover = false;
451
- } else if (opts.org || opts.date) {
452
- gongmun.cover = { ...opts.org ? { org: opts.org } : {}, ...opts.date ? { date: opts.date } : {} };
453
- } else if (opts.cover === true) {
454
- gongmun.cover = true;
455
- }
456
- if (opts.toc !== void 0) gongmun.toc = opts.toc;
457
- if (opts.approval) gongmun.approval = String(opts.approval).split(",").map((s) => s.trim()).filter(Boolean);
458
- if (opts.pageNumbers !== void 0) gongmun.pageNumbers = opts.pageNumbers;
459
- if (opts.endMark !== void 0) gongmun.endMark = opts.endMark;
460
- if (opts.bodyTitleBox === false) gongmun.bodyTitleBox = false;
461
- if (opts.h2Marker) {
462
- if (!["box", "number", "none"].includes(opts.h2Marker)) {
463
- process.stderr.write(`[kordoc] --h2-marker \uB294 box/number/none
464
- `);
465
- process.exit(1);
466
- }
467
- gongmun.h2Marker = opts.h2Marker;
468
- }
469
- if (opts.font) {
470
- if (opts.font !== "myeongjo" && opts.font !== "gothic") {
471
- process.stderr.write(`[kordoc] --font \uC740 myeongjo \uB610\uB294 gothic
468
+ const enumCheck = (flag, value, allowed) => {
469
+ if (value === void 0) return void 0;
470
+ if (!allowed.includes(String(value))) {
471
+ process.stderr.write(`[kordoc] ${flag} \uB294 ${allowed.join("/")}
472
472
  `);
473
473
  process.exit(1);
474
474
  }
475
- gongmun.bodyFont = opts.font;
476
- }
477
- if (opts.pt) gongmun.bodyPt = Number(opts.pt);
478
- if (opts.lineSpacing) gongmun.lineSpacing = Number(opts.lineSpacing);
479
- const parseKv = (spec) => Object.fromEntries(spec.split(",").map((p) => p.split("=").map((s) => s.trim())).filter((kv) => kv.length === 2 && kv[0] && kv[1]));
480
- if (opts.fonts) gongmun.fonts = parseKv(String(opts.fonts));
481
- if (opts.sizes) {
482
- gongmun.sizes = Object.fromEntries(
483
- Object.entries(parseKv(String(opts.sizes))).map(([k, v]) => [k, Number(v)]).filter(([, v]) => Number.isFinite(v))
484
- );
485
- }
486
- if (opts.bullet2) {
487
- if (opts.bullet2 !== "\u3147" && opts.bullet2 !== "\u25CB") {
488
- process.stderr.write(`[kordoc] --bullet2 \uB294 \u3147 \uB610\uB294 \u25CB
489
- `);
490
- process.exit(1);
475
+ return value;
476
+ };
477
+ const parseKv = (spec, flag) => {
478
+ const out = {};
479
+ for (const piece of spec.split(",")) {
480
+ const p = piece.trim();
481
+ if (!p) continue;
482
+ const eq = p.indexOf("=");
483
+ const key = eq > 0 ? p.slice(0, eq).trim() : "";
484
+ const value = eq > 0 ? p.slice(eq + 1).trim() : "";
485
+ if (!key || !value) {
486
+ process.stderr.write(`[kordoc] ${flag}: 'key=value' \uD615\uC2DD\uC774 \uC544\uB2CC \uC870\uAC01 \uBB34\uC2DC \u2014 "${p}" (\uAC12\uC5D0 \uC27C\uD45C\uB294 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4)
487
+ `);
488
+ continue;
489
+ }
490
+ out[key] = value;
491
491
  }
492
- gongmun.bullet2 = opts.bullet2;
493
- }
494
- if (opts.suppressSingle) gongmun.suppressSingle = true;
495
- if (opts.docHead) gongmun.docHead = parseKv(String(opts.docHead));
496
- if (opts.docFoot) gongmun.docFoot = parseKv(String(opts.docFoot));
497
- if (opts.reportInfo) gongmun.reportInfo = String(opts.reportInfo);
498
- if (opts.noticeHead) gongmun.noticeHead = parseKv(String(opts.noticeHead));
499
- if (opts.pressHead || opts.pressSub) {
500
- const kv = opts.pressHead ? parseKv(String(opts.pressHead)) : {};
501
- gongmun.press = {
502
- release: kv.release,
503
- distribute: kv.distribute,
492
+ return out;
493
+ };
494
+ const pressKv = opts.pressHead ? parseKv(String(opts.pressHead), "--press-head") : {};
495
+ gongmun = buildGongmunOptions({
496
+ preset,
497
+ font: enumCheck("--font", opts.font, BODY_FONTS),
498
+ bodyPt: opts.pt ? Number(opts.pt) : void 0,
499
+ lineSpacing: opts.lineSpacing ? Number(opts.lineSpacing) : void 0,
500
+ org: opts.org,
501
+ date: opts.date,
502
+ cover: opts.cover,
503
+ toc: opts.toc,
504
+ approval: opts.approval ? String(opts.approval).split(",").map((s) => s.trim()).filter(Boolean) : void 0,
505
+ pageNumbers: opts.pageNumbers,
506
+ endMark: opts.endMark,
507
+ // --no-body-title-box 단독 플래그 — commander 기본 true는 "미지정"으로 정돈
508
+ bodyTitleBox: opts.bodyTitleBox === false ? false : void 0,
509
+ h2Marker: enumCheck("--h2-marker", opts.h2Marker, H2_MARKERS),
510
+ fonts: opts.fonts ? parseKv(String(opts.fonts), "--fonts") : void 0,
511
+ sizes: opts.sizes ? Object.fromEntries(
512
+ Object.entries(parseKv(String(opts.sizes), "--sizes")).map(([k, v]) => [k, Number(v)]).filter(([, v]) => Number.isFinite(v))
513
+ ) : void 0,
514
+ bullet2: enumCheck("--bullet2", opts.bullet2, BULLET2_CHARS),
515
+ suppressSingle: opts.suppressSingle ? true : void 0,
516
+ docHead: opts.docHead ? parseKv(String(opts.docHead), "--doc-head") : void 0,
517
+ docFoot: opts.docFoot ? parseKv(String(opts.docFoot), "--doc-foot") : void 0,
518
+ reportInfo: opts.reportInfo ? String(opts.reportInfo) : void 0,
519
+ noticeHead: opts.noticeHead ? parseKv(String(opts.noticeHead), "--notice-head") : void 0,
520
+ press: opts.pressHead || opts.pressSub ? {
521
+ release: pressKv.release,
522
+ distribute: pressKv.distribute,
504
523
  sub: opts.pressSub ? String(opts.pressSub).split(";").map((s) => s.trim()).filter(Boolean) : void 0,
505
- contact: kv.dept || kv.manager || kv.phone ? { dept: kv.dept, manager: kv.manager, phone: kv.phone } : void 0
506
- };
507
- }
524
+ contact: pressKv.dept || pressKv.manager || pressKv.phone ? { dept: pressKv.dept, manager: pressKv.manager, phone: pressKv.phone } : void 0
525
+ } : void 0
526
+ });
508
527
  }
509
528
  if (gongmun?.fonts && !silent) {
510
529
  for (const w of unknownFontWarnings(gongmun.fonts)) process.stderr.write(`[kordoc] ${w}
530
+ `);
531
+ }
532
+ if (gongmun && !silent) {
533
+ for (const w of incompatibleGongmunWarnings(gongmun)) process.stderr.write(`[kordoc] \u26A0 ${w}
511
534
  `);
512
535
  }
513
536
  if (gongmun && !silent) {
514
537
  for (const w of gongmunLintWarnings(md, 5)) process.stderr.write(`[kordoc] \u26A0 ${w}
515
538
  `);
516
539
  }
517
- const buf = await markdownToHwpx(md, gongmun ? { gongmun } : void 0);
540
+ let profile;
541
+ if (opts.profile) {
542
+ profile = parseFormatProfileJson(readFileSync(resolve(String(opts.profile)), "utf-8"));
543
+ if (!silent) process.stderr.write(`[kordoc] \uC11C\uC2DD \uD504\uB85C\uD544 \uC801\uC6A9: \uD45C ${profile.tables.length}\uAC1C (${opts.profile})
544
+ `);
545
+ }
546
+ const buf = await markdownToHwpx(md, gongmun || profile ? { ...gongmun ? { gongmun } : {}, ...profile ? { profile } : {} } : void 0);
518
547
  const outPath = resolve(output ?? (markdown === "-" ? `${baseName}.hwpx` : markdown.replace(/\.(md|markdown|txt)$/i, "") + ".hwpx"));
519
548
  mkdirSync(dirname(outPath), { recursive: true });
520
549
  writeFileSync(outPath, Buffer.from(buf));
@@ -529,6 +558,24 @@ program.command("generate <markdown>").alias("gen").description("\uB9C8\uD06C\uB
529
558
  process.exit(1);
530
559
  }
531
560
  });
561
+ program.command("profile <file>").description("HWPX \uD45C \uC11C\uC2DD \uD504\uB85C\uD544 \uCD94\uCD9C \u2014 \uCC38\uC870 \uBB38\uC11C\uC758 \uD45C \uD14C\uB450\uB9AC\xB7\uC74C\uC601\xB7\uC5F4\uD3ED\xB7\uC140 \uAE00\uAF34\uC744 JSON\uC73C\uB85C (generate --profile\uB85C \uC7AC\uD604) \u2014 kordoc profile \uCC38\uC870.hwpx -o \uC11C\uC2DD.json").option("-o, --output <path>", "\uCD9C\uB825 JSON \uACBD\uB85C (\uAE30\uBCF8: <\uC785\uB825>.profile.json)").option("--silent", "\uC9C4\uD589 \uBA54\uC2DC\uC9C0 \uC228\uAE30\uAE30").action(async (file, opts) => {
562
+ try {
563
+ const rootOpts = program.opts();
564
+ const output = opts.output ?? rootOpts.output;
565
+ const silent = opts.silent ?? rootOpts.silent;
566
+ const absPath = resolve(file);
567
+ const profile = await hwpxToProfile(readFileSync(absPath));
568
+ const outPath = resolve(output ?? absPath.replace(/\.hwpx$/i, "") + ".profile.json");
569
+ mkdirSync(dirname(outPath), { recursive: true });
570
+ writeFileSync(outPath, JSON.stringify(profile, null, 2));
571
+ if (!silent) process.stderr.write(`[kordoc] \uC11C\uC2DD \uD504\uB85C\uD544 \uCD94\uCD9C: \uD45C ${profile.tables.length}\uAC1C \u2192 ${outPath}
572
+ `);
573
+ } catch (err) {
574
+ process.stderr.write(`[kordoc] \uC624\uB958: ${sanitizeError(err)}
575
+ `);
576
+ process.exit(1);
577
+ }
578
+ });
532
579
  program.command("lint <file>").description("\uACF5\uBB38\uC11C \uD45C\uAE30\uBC95 \uAC80\uC218 \u2014 \uB0A0\uC9DC\xB7\uC2DC\uAC04\xB7\uAE08\uC561\xB7\uBD99\uC784 \uB4F1 \uD589\uC815\uC5C5\uBB34\uC6B4\uC601 \uD3B8\uB78C \uD45C\uAE30\uBC95 (md/txt, '-'=stdin). error \uC788\uC73C\uBA74 exit 1").option("--json", "JSON \uCD9C\uB825").action((file, opts) => {
533
580
  try {
534
581
  const text = file === "-" ? readFileSync(0, "utf-8") : readFileSync(resolve(file), "utf-8");
@@ -556,7 +603,7 @@ program.command("render <file>").description("\uB808\uC774\uC544\uC6C3 \uBCF4\uC
556
603
  const rootOpts = program.opts();
557
604
  const output = opts.output ?? rootOpts.output;
558
605
  const silent = opts.silent ?? rootOpts.silent;
559
- const { renderHwpxToSvg } = await import("./render-JPP7YTV3.js");
606
+ const { renderHwpxToSvg } = await import("./render-TTOG6CHW.js");
560
607
  const absPath = resolve(file);
561
608
  const buffer = readFileSync(absPath);
562
609
  const highlights = opts.highlight ? String(opts.highlight).split(",") : void 0;
@@ -578,7 +625,7 @@ program.command("render <file>").description("\uB808\uC774\uC544\uC6C3 \uBCF4\uC
578
625
  });
579
626
  program.command("render-worker").description("persistent \uB80C\uB354 \uC6CC\uCEE4 \u2014 stdin NDJSON \uC694\uCCAD \u2192 \uC870\uD310 SVG \uD30C\uC77C \uCD9C\uB825 (\uD504\uB85C\uC138\uC2A4 \uC720\uC9C0, \uCF5C\uB4DC\uC2A4\uD0C0\uD2B8 \uC81C\uAC70)").action(async () => {
580
627
  const { createInterface } = await import("readline");
581
- const { renderHwpxToSvg } = await import("./render-JPP7YTV3.js");
628
+ const { renderHwpxToSvg } = await import("./render-TTOG6CHW.js");
582
629
  const rl = createInterface({ input: process.stdin });
583
630
  const write = (o) => void process.stdout.write(JSON.stringify(o) + "\n");
584
631
  write({ ready: true, version: VERSION });