kordoc 3.4.1 → 3.5.1

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 (85) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +590 -584
  3. package/dist/{-YOQGQ22S.js → -BNJMPTQ2.js} +9 -5
  4. package/dist/{chunk-X34YWRL5.cjs → chunk-DCZVOIEO.cjs} +1 -1
  5. package/dist/chunk-DCZVOIEO.cjs.map +1 -0
  6. package/dist/{chunk-AIQ3ISQU.js → chunk-GE43BE46.js} +1 -1
  7. package/dist/chunk-GE43BE46.js.map +1 -0
  8. package/dist/{chunk-CGHPBI3X.js → chunk-GMPDZD7Q.js} +206 -42
  9. package/dist/chunk-GMPDZD7Q.js.map +1 -0
  10. package/dist/chunk-GS7T56RP.cjs.map +1 -1
  11. package/dist/{chunk-FPFB7AFX.js → chunk-IKAXUVSU.js} +16 -2
  12. package/dist/chunk-IKAXUVSU.js.map +1 -0
  13. package/dist/{chunk-CFQLK3LM.js → chunk-MEPHGCPQ.js} +1 -1
  14. package/dist/chunk-MEPHGCPQ.js.map +1 -0
  15. package/dist/{chunk-3TBUDJDE.js → chunk-MOL7MDBG.js} +1 -1
  16. package/dist/chunk-MOL7MDBG.js.map +1 -0
  17. package/dist/{chunk-SE3KEL4G.js → chunk-OXOPDVNV.js} +16 -2
  18. package/dist/chunk-OXOPDVNV.js.map +1 -0
  19. package/dist/{chunk-W7JHJQ2O.cjs → chunk-RWSGBXSA.cjs} +25 -11
  20. package/dist/chunk-RWSGBXSA.cjs.map +1 -0
  21. package/dist/cli.js +10 -27
  22. package/dist/cli.js.map +1 -1
  23. package/dist/{detect-NTR2FUEG.js → detect-RI2MQ33K.js} +2 -2
  24. package/dist/{formula-ZYUDQHD3.cjs → formula-5NKVS2LR.cjs} +1 -1
  25. package/dist/formula-5NKVS2LR.cjs.map +1 -0
  26. package/dist/{formula-KOQV353G.js → formula-JCNF43NE.js} +1 -1
  27. package/dist/formula-JCNF43NE.js.map +1 -0
  28. package/dist/{formula-MEDGYQXS.js → formula-RXVSQPXI.js} +1 -1
  29. package/dist/formula-RXVSQPXI.js.map +1 -0
  30. package/dist/index.cjs +377 -213
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +9 -3
  33. package/dist/index.d.ts +9 -3
  34. package/dist/index.js +204 -40
  35. package/dist/index.js.map +1 -1
  36. package/dist/mcp.js +42 -6
  37. package/dist/mcp.js.map +1 -1
  38. package/dist/page-range-737B4EZW.js +8 -0
  39. package/dist/page-range-LNMXJU6W.js +7 -0
  40. package/dist/page-range-P7SDW6LR.cjs +8 -0
  41. package/dist/page-range-P7SDW6LR.cjs.map +1 -0
  42. package/dist/{parser-5HDZZWDH.js → parser-GHZMDUGG.js} +5 -5
  43. package/dist/parser-GHZMDUGG.js.map +1 -0
  44. package/dist/{parser-ET64VEEX.js → parser-XXQAMYGV.js} +5 -5
  45. package/dist/parser-XXQAMYGV.js.map +1 -0
  46. package/dist/{parser-JAZCZJI6.cjs → parser-ZLFOUVR4.cjs} +18 -18
  47. package/dist/parser-ZLFOUVR4.cjs.map +1 -0
  48. package/dist/{provider-H4WWSPOV.js → provider-4ZJKV3DC.js} +1 -1
  49. package/dist/provider-4ZJKV3DC.js.map +1 -0
  50. package/dist/{provider-7H4CPZYS.js → provider-AKROB7WQ.js} +1 -1
  51. package/dist/provider-AKROB7WQ.js.map +1 -0
  52. package/dist/{provider-5K7O3Z2O.cjs → provider-G4C2V2PD.cjs} +1 -1
  53. package/dist/provider-G4C2V2PD.cjs.map +1 -0
  54. package/dist/{setup-QSJ2INNS.js → setup-57FB3LSP.js} +1 -1
  55. package/dist/setup-57FB3LSP.js.map +1 -0
  56. package/dist/{watch-56GNC2YN.js → watch-IN3YN72W.js} +5 -5
  57. package/dist/watch-IN3YN72W.js.map +1 -0
  58. package/package.json +98 -98
  59. package/dist/chunk-3TBUDJDE.js.map +0 -1
  60. package/dist/chunk-AIQ3ISQU.js.map +0 -1
  61. package/dist/chunk-CFQLK3LM.js.map +0 -1
  62. package/dist/chunk-CGHPBI3X.js.map +0 -1
  63. package/dist/chunk-FPFB7AFX.js.map +0 -1
  64. package/dist/chunk-SE3KEL4G.js.map +0 -1
  65. package/dist/chunk-W7JHJQ2O.cjs.map +0 -1
  66. package/dist/chunk-X34YWRL5.cjs.map +0 -1
  67. package/dist/formula-KOQV353G.js.map +0 -1
  68. package/dist/formula-MEDGYQXS.js.map +0 -1
  69. package/dist/formula-ZYUDQHD3.cjs.map +0 -1
  70. package/dist/page-range-CIRRJPXJ.js +0 -7
  71. package/dist/page-range-OF5I4PQY.js +0 -8
  72. package/dist/page-range-TPIRSA3J.cjs +0 -8
  73. package/dist/page-range-TPIRSA3J.cjs.map +0 -1
  74. package/dist/parser-5HDZZWDH.js.map +0 -1
  75. package/dist/parser-ET64VEEX.js.map +0 -1
  76. package/dist/parser-JAZCZJI6.cjs.map +0 -1
  77. package/dist/provider-5K7O3Z2O.cjs.map +0 -1
  78. package/dist/provider-7H4CPZYS.js.map +0 -1
  79. package/dist/provider-H4WWSPOV.js.map +0 -1
  80. package/dist/setup-QSJ2INNS.js.map +0 -1
  81. package/dist/watch-56GNC2YN.js.map +0 -1
  82. /package/dist/{-YOQGQ22S.js.map → -BNJMPTQ2.js.map} +0 -0
  83. /package/dist/{detect-NTR2FUEG.js.map → detect-RI2MQ33K.js.map} +0 -0
  84. /package/dist/{page-range-CIRRJPXJ.js.map → page-range-737B4EZW.js.map} +0 -0
  85. /package/dist/{page-range-OF5I4PQY.js.map → page-range-LNMXJU6W.js.map} +0 -0
package/dist/index.d.cts CHANGED
@@ -435,10 +435,12 @@ declare function fillHwpx(hwpxBuffer: ArrayBuffer, values: Record<string, string
435
435
  type GongmunPreset = "official" | "report" | "plan" | "notice" | "minutes";
436
436
  type GongmunNumbering = "standard" | "report";
437
437
  type GongmunFont = "myeongjo" | "gothic";
438
+ /** 프리셋 입력값 — 영문 키 또는 한글 별칭(기안문·보고서·계획서·통지·회의록 등) */
439
+ type GongmunPresetInput = GongmunPreset | "기안문" | "시행문" | "공문" | "공문서" | "보고서" | "계획서" | "계획" | "통지" | "알림" | "안내" | "회의록";
438
440
  /** 공문서 모드 옵션 (전부 선택 — 프리셋 기본값을 개별 override) */
439
441
  interface GongmunOptions {
440
- /** 문서 종류 프리셋. 기본 'official'(일반 기안문) */
441
- preset?: GongmunPreset;
442
+ /** 문서 종류 프리셋(영문 키 또는 한글 별칭). 기본 'official'(일반 기안문) */
443
+ preset?: GongmunPresetInput;
442
444
  /** 본문 글꼴. 'myeongjo'=함초롬바탕(명조, 보고서·대외공문 관행) / 'gothic'=맑은 고딕(전자결재 기본) */
443
445
  bodyFont?: GongmunFont;
444
446
  /** 본문 글자 크기(pt). 기본 15 */
@@ -457,6 +459,10 @@ interface GongmunOptions {
457
459
  /** 문서 제목(첫 h1)을 가운데 정렬. 기본 true (행정기관명·보고서 제목) */
458
460
  centerTitle?: boolean;
459
461
  }
462
+ /** 프리셋 별칭(한글/영문) → 내부 preset 키. CLI·라이브러리 공용 */
463
+ declare const PRESET_ALIAS: Record<string, GongmunPreset>;
464
+ /** 프리셋 입력(영문 키 또는 한글 별칭)을 내부 GongmunPreset로 정규화. 미상은 'official' */
465
+ declare function normalizeGongmunPreset(preset?: string): GongmunPreset;
460
466
 
461
467
  /**
462
468
  * Markdown → HWPX 역변환
@@ -903,4 +909,4 @@ interface FillFormOutput {
903
909
  */
904
910
  declare function fillForm(input: string | ArrayBuffer | Buffer, values: Record<string, string>, outputFormat?: FillOutputFormat): Promise<FillFormOutput>;
905
911
 
906
- export { type BlockCapability, type BlockCapabilityInfo, type BlockDiff, type BlockEdit, type BlockSourceRef, type BoundingBox, type CellCapability, type CellContext, type CellDiff, type DiffChangeType, type DiffResult, type DocumentMetadata, type DocumentQualitySummary, type ErrorCode, type ExtractedImage, type FileType, type FillFormOutput, type FillOutputFormat, type FillResult, type FormField, type FormFieldSchema, type FormFieldType, type FormResult, type FormSchemaResult, type GongmunFont, type GongmunNumbering, type GongmunOptions, type GongmunPreset, type HwpxFillResult, HwpxSession, type HwpxTheme, type IRBlock, type IRBlockType, type IRCell, type IRTable, type ImageData, type InlineStyle, type MarkdownToHwpxOptions, type OcrProvider, type OutlineItem, type PageMargin, type PageQuality, type ParseFailure, type ParseOptions, type ParseResult, type ParseSuccess, type ParseWarning, type PatchOptions, type PatchResult, type PatchSkip, type PrintOptions, type PrintPreset, type ScanCell, type ScanParaKind, type ScanParagraph, type ScanTable, type SectionScan, type SpliceEdit, type TRange, VERSION, type WarningCode, type WatchOptions, applySplices, blocksToMarkdown, blocksToPdf, buildParagraphSplices, buildRangeSplices, compare, detectFormat, detectOle2Format, detectZipFormat, diffBlocks, extractFormFields, extractFormSchema, fillForm, fillFormFields, fillHwpx, inferFieldType, isHwpxFile, isLabelCell, isOldHwpFile, isPdfFile, isZipFile, markdownToHwpx, markdownToPdf, openHwpxDocument, parse, parseDocx, parseHwp, parseHwp3, parseHwpml, parseHwpx, parsePdf, parseXls, parseXlsx, patchHwp, patchHwpx, patchHwpxBlocks, renderHtml, scanSectionXml };
912
+ export { type BlockCapability, type BlockCapabilityInfo, type BlockDiff, type BlockEdit, type BlockSourceRef, type BoundingBox, type CellCapability, type CellContext, type CellDiff, type DiffChangeType, type DiffResult, type DocumentMetadata, type DocumentQualitySummary, type ErrorCode, type ExtractedImage, type FileType, type FillFormOutput, type FillOutputFormat, type FillResult, type FormField, type FormFieldSchema, type FormFieldType, type FormResult, type FormSchemaResult, type GongmunFont, type GongmunNumbering, type GongmunOptions, type GongmunPreset, type GongmunPresetInput, type HwpxFillResult, HwpxSession, type HwpxTheme, type IRBlock, type IRBlockType, type IRCell, type IRTable, type ImageData, type InlineStyle, type MarkdownToHwpxOptions, type OcrProvider, type OutlineItem, PRESET_ALIAS, type PageMargin, type PageQuality, type ParseFailure, type ParseOptions, type ParseResult, type ParseSuccess, type ParseWarning, type PatchOptions, type PatchResult, type PatchSkip, type PrintOptions, type PrintPreset, type ScanCell, type ScanParaKind, type ScanParagraph, type ScanTable, type SectionScan, type SpliceEdit, type TRange, VERSION, type WarningCode, type WatchOptions, applySplices, blocksToMarkdown, blocksToPdf, buildParagraphSplices, buildRangeSplices, compare, detectFormat, detectOle2Format, detectZipFormat, diffBlocks, extractFormFields, extractFormSchema, fillForm, fillFormFields, fillHwpx, inferFieldType, isHwpxFile, isLabelCell, isOldHwpFile, isPdfFile, isZipFile, markdownToHwpx, markdownToPdf, normalizeGongmunPreset, openHwpxDocument, parse, parseDocx, parseHwp, parseHwp3, parseHwpml, parseHwpx, parsePdf, parseXls, parseXlsx, patchHwp, patchHwpx, patchHwpxBlocks, renderHtml, scanSectionXml };
package/dist/index.d.ts CHANGED
@@ -435,10 +435,12 @@ declare function fillHwpx(hwpxBuffer: ArrayBuffer, values: Record<string, string
435
435
  type GongmunPreset = "official" | "report" | "plan" | "notice" | "minutes";
436
436
  type GongmunNumbering = "standard" | "report";
437
437
  type GongmunFont = "myeongjo" | "gothic";
438
+ /** 프리셋 입력값 — 영문 키 또는 한글 별칭(기안문·보고서·계획서·통지·회의록 등) */
439
+ type GongmunPresetInput = GongmunPreset | "기안문" | "시행문" | "공문" | "공문서" | "보고서" | "계획서" | "계획" | "통지" | "알림" | "안내" | "회의록";
438
440
  /** 공문서 모드 옵션 (전부 선택 — 프리셋 기본값을 개별 override) */
439
441
  interface GongmunOptions {
440
- /** 문서 종류 프리셋. 기본 'official'(일반 기안문) */
441
- preset?: GongmunPreset;
442
+ /** 문서 종류 프리셋(영문 키 또는 한글 별칭). 기본 'official'(일반 기안문) */
443
+ preset?: GongmunPresetInput;
442
444
  /** 본문 글꼴. 'myeongjo'=함초롬바탕(명조, 보고서·대외공문 관행) / 'gothic'=맑은 고딕(전자결재 기본) */
443
445
  bodyFont?: GongmunFont;
444
446
  /** 본문 글자 크기(pt). 기본 15 */
@@ -457,6 +459,10 @@ interface GongmunOptions {
457
459
  /** 문서 제목(첫 h1)을 가운데 정렬. 기본 true (행정기관명·보고서 제목) */
458
460
  centerTitle?: boolean;
459
461
  }
462
+ /** 프리셋 별칭(한글/영문) → 내부 preset 키. CLI·라이브러리 공용 */
463
+ declare const PRESET_ALIAS: Record<string, GongmunPreset>;
464
+ /** 프리셋 입력(영문 키 또는 한글 별칭)을 내부 GongmunPreset로 정규화. 미상은 'official' */
465
+ declare function normalizeGongmunPreset(preset?: string): GongmunPreset;
460
466
 
461
467
  /**
462
468
  * Markdown → HWPX 역변환
@@ -903,4 +909,4 @@ interface FillFormOutput {
903
909
  */
904
910
  declare function fillForm(input: string | ArrayBuffer | Buffer, values: Record<string, string>, outputFormat?: FillOutputFormat): Promise<FillFormOutput>;
905
911
 
906
- export { type BlockCapability, type BlockCapabilityInfo, type BlockDiff, type BlockEdit, type BlockSourceRef, type BoundingBox, type CellCapability, type CellContext, type CellDiff, type DiffChangeType, type DiffResult, type DocumentMetadata, type DocumentQualitySummary, type ErrorCode, type ExtractedImage, type FileType, type FillFormOutput, type FillOutputFormat, type FillResult, type FormField, type FormFieldSchema, type FormFieldType, type FormResult, type FormSchemaResult, type GongmunFont, type GongmunNumbering, type GongmunOptions, type GongmunPreset, type HwpxFillResult, HwpxSession, type HwpxTheme, type IRBlock, type IRBlockType, type IRCell, type IRTable, type ImageData, type InlineStyle, type MarkdownToHwpxOptions, type OcrProvider, type OutlineItem, type PageMargin, type PageQuality, type ParseFailure, type ParseOptions, type ParseResult, type ParseSuccess, type ParseWarning, type PatchOptions, type PatchResult, type PatchSkip, type PrintOptions, type PrintPreset, type ScanCell, type ScanParaKind, type ScanParagraph, type ScanTable, type SectionScan, type SpliceEdit, type TRange, VERSION, type WarningCode, type WatchOptions, applySplices, blocksToMarkdown, blocksToPdf, buildParagraphSplices, buildRangeSplices, compare, detectFormat, detectOle2Format, detectZipFormat, diffBlocks, extractFormFields, extractFormSchema, fillForm, fillFormFields, fillHwpx, inferFieldType, isHwpxFile, isLabelCell, isOldHwpFile, isPdfFile, isZipFile, markdownToHwpx, markdownToPdf, openHwpxDocument, parse, parseDocx, parseHwp, parseHwp3, parseHwpml, parseHwpx, parsePdf, parseXls, parseXlsx, patchHwp, patchHwpx, patchHwpxBlocks, renderHtml, scanSectionXml };
912
+ export { type BlockCapability, type BlockCapabilityInfo, type BlockDiff, type BlockEdit, type BlockSourceRef, type BoundingBox, type CellCapability, type CellContext, type CellDiff, type DiffChangeType, type DiffResult, type DocumentMetadata, type DocumentQualitySummary, type ErrorCode, type ExtractedImage, type FileType, type FillFormOutput, type FillOutputFormat, type FillResult, type FormField, type FormFieldSchema, type FormFieldType, type FormResult, type FormSchemaResult, type GongmunFont, type GongmunNumbering, type GongmunOptions, type GongmunPreset, type GongmunPresetInput, type HwpxFillResult, HwpxSession, type HwpxTheme, type IRBlock, type IRBlockType, type IRCell, type IRTable, type ImageData, type InlineStyle, type MarkdownToHwpxOptions, type OcrProvider, type OutlineItem, PRESET_ALIAS, type PageMargin, type PageQuality, type ParseFailure, type ParseOptions, type ParseResult, type ParseSuccess, type ParseWarning, type PatchOptions, type PatchResult, type PatchSkip, type PrintOptions, type PrintPreset, type ScanCell, type ScanParaKind, type ScanParagraph, type ScanTable, type SectionScan, type SpliceEdit, type TRange, VERSION, type WarningCode, type WatchOptions, applySplices, blocksToMarkdown, blocksToPdf, buildParagraphSplices, buildRangeSplices, compare, detectFormat, detectOle2Format, detectZipFormat, diffBlocks, extractFormFields, extractFormSchema, fillForm, fillFormFields, fillHwpx, inferFieldType, isHwpxFile, isLabelCell, isOldHwpFile, isPdfFile, isZipFile, markdownToHwpx, markdownToPdf, normalizeGongmunPreset, openHwpxDocument, parse, parseDocx, parseHwp, parseHwp3, parseHwpml, parseHwpx, parsePdf, parseXls, parseXlsx, patchHwp, patchHwpx, patchHwpxBlocks, renderHtml, scanSectionXml };
package/dist/index.js CHANGED
@@ -9,18 +9,20 @@ import {
9
9
  blocksToMarkdown,
10
10
  buildTable,
11
11
  classifyError,
12
+ compareSectionPaths,
12
13
  convertTableToText,
13
14
  flattenLayoutTables,
14
15
  isPathTraversal,
15
16
  mapPuaText,
17
+ normalizeSectionHref,
16
18
  precheckZipSize,
17
19
  sanitizeHref,
18
20
  stripDtd,
19
21
  toArrayBuffer
20
- } from "./chunk-SE3KEL4G.js";
22
+ } from "./chunk-OXOPDVNV.js";
21
23
  import {
22
24
  parsePageRange
23
- } from "./chunk-AIQ3ISQU.js";
25
+ } from "./chunk-GE43BE46.js";
24
26
 
25
27
  // src/index.ts
26
28
  import { readFile } from "fs/promises";
@@ -657,14 +659,27 @@ function findBrackets(eqString, startIdx, direction) {
657
659
  const [s, e] = findBrackets(flipped, newStartIdx, 1);
658
660
  return [flipped.length - e, flipped.length - s];
659
661
  }
660
- function findOutterBrackets(eqString, startIdx) {
661
- let idx = startIdx;
662
- while (true) {
663
- idx -= 1;
664
- if (idx < 0) throw new Error("cannot find bracket");
665
- if (eqString[idx] === "{") break;
662
+ function findEnclosingBrackets(eqString, startIdx) {
663
+ let depth = 0;
664
+ for (let idx = startIdx - 1; idx >= 0; idx--) {
665
+ const ch = eqString[idx];
666
+ if (ch === "}") {
667
+ depth += 1;
668
+ } else if (ch === "{") {
669
+ if (depth > 0) {
670
+ depth -= 1;
671
+ continue;
672
+ }
673
+ try {
674
+ const [start, end] = findBrackets(eqString, idx, 1);
675
+ if (start === idx && end > startIdx) return [start, end];
676
+ } catch {
677
+ return null;
678
+ }
679
+ return null;
680
+ }
666
681
  }
667
- return findBrackets(eqString, idx, 1);
682
+ return null;
668
683
  }
669
684
  function replaceFrac(eqString) {
670
685
  const hmlFrac = "over";
@@ -717,8 +732,9 @@ function replaceAllMatrix(eqString) {
717
732
  const elem = replaceElements(input.slice(eStart, eEnd));
718
733
  let beforeMat;
719
734
  let afterMat;
720
- if (matElem.removeOutterBrackets) {
721
- const [bStart, bEnd] = findOutterBrackets(input, cursor);
735
+ const outer = matElem.removeOutterBrackets ? findEnclosingBrackets(input, cursor) : null;
736
+ if (outer && outer[1] >= eEnd) {
737
+ const [bStart, bEnd] = outer;
722
738
  beforeMat = input.slice(0, bStart);
723
739
  afterMat = input.slice(bEnd);
724
740
  } else {
@@ -744,10 +760,11 @@ function replaceAllBar(eqString) {
744
760
  if (cursor === -1) break;
745
761
  try {
746
762
  const [eStart, eEnd] = findBrackets(input, cursor, 1);
747
- const [bStart, bEnd] = findOutterBrackets(input, cursor);
748
763
  const elem = input.slice(eStart, eEnd);
749
- const beforeBar = input.slice(0, bStart);
750
- const afterBar = input.slice(bEnd);
764
+ const outer = findEnclosingBrackets(input, cursor);
765
+ const [replaceStart, replaceEnd] = outer && outer[1] >= eEnd ? outer : [cursor, eEnd];
766
+ const beforeBar = input.slice(0, replaceStart);
767
+ const afterBar = input.slice(replaceEnd);
751
768
  input = beforeBar + barElem + elem + afterBar;
752
769
  } catch {
753
770
  return input;
@@ -1369,24 +1386,19 @@ async function resolveSectionPaths(zip) {
1369
1386
  if (paths.length > 0) return paths;
1370
1387
  }
1371
1388
  const sectionFiles = zip.file(/[Ss]ection\d+\.xml$/);
1372
- return sectionFiles.map((f) => f.name).sort();
1389
+ return sectionFiles.map((f) => f.name).sort(compareSectionPaths);
1373
1390
  }
1374
1391
  function parseSectionPathsFromManifest(xml) {
1375
1392
  const parser = createXmlParser();
1376
1393
  const doc = parser.parseFromString(stripDtd(xml), "text/xml");
1377
1394
  const items = doc.getElementsByTagName("opf:item");
1378
1395
  const spine = doc.getElementsByTagName("opf:itemref");
1379
- const isSectionId = (id) => /^s/i.test(id) || id.toLowerCase().includes("section");
1380
1396
  const idToHref = /* @__PURE__ */ new Map();
1381
1397
  for (let i = 0; i < items.length; i++) {
1382
1398
  const item = items[i];
1383
1399
  const id = item.getAttribute("id") || "";
1384
- let href = item.getAttribute("href") || "";
1385
- const mediaType = item.getAttribute("media-type") || "";
1386
- if (!isSectionId(id) && !mediaType.includes("xml")) continue;
1387
- if (!href.startsWith("/") && !href.startsWith("Contents/") && isSectionId(id))
1388
- href = "Contents/" + href;
1389
- idToHref.set(id, href);
1400
+ const href = normalizeSectionHref(item.getAttribute("href") || "");
1401
+ if (id && href) idToHref.set(id, href);
1390
1402
  }
1391
1403
  if (spine.length > 0) {
1392
1404
  const ordered = [];
@@ -1396,7 +1408,7 @@ function parseSectionPathsFromManifest(xml) {
1396
1408
  }
1397
1409
  if (ordered.length > 0) return ordered;
1398
1410
  }
1399
- return Array.from(idToHref.entries()).filter(([id]) => isSectionId(id)).sort((a, b) => a[0].localeCompare(b[0])).map(([, href]) => href);
1411
+ return Array.from(idToHref.values()).sort(compareSectionPaths);
1400
1412
  }
1401
1413
  function detectHwpxHeadings(blocks, styleMap) {
1402
1414
  if (blocks.some((b) => b.type === "heading")) return;
@@ -16907,7 +16919,7 @@ async function parseXlsxDocument(buffer, options) {
16907
16919
  }
16908
16920
  let pageFilter = null;
16909
16921
  if (options?.pages) {
16910
- const { parsePageRange: parsePageRange2 } = await import("./page-range-CIRRJPXJ.js");
16922
+ const { parsePageRange: parsePageRange2 } = await import("./page-range-LNMXJU6W.js");
16911
16923
  pageFilter = parsePageRange2(options.pages, sheets.length);
16912
16924
  }
16913
16925
  const blocks = [];
@@ -17545,7 +17557,7 @@ async function parseXlsDocument(buffer, options) {
17545
17557
  const totalSheets = Math.min(globals.sheets.length, MAX_SHEETS2);
17546
17558
  let pageFilter = null;
17547
17559
  if (options?.pages) {
17548
- const { parsePageRange: parsePageRange2 } = await import("./page-range-CIRRJPXJ.js");
17560
+ const { parsePageRange: parsePageRange2 } = await import("./page-range-LNMXJU6W.js");
17549
17561
  pageFilter = parsePageRange2(options.pages, totalSheets);
17550
17562
  }
17551
17563
  const allBlocks = [];
@@ -20010,8 +20022,30 @@ var PRESET_DEFAULTS = {
20010
20022
  notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
20011
20023
  minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" }
20012
20024
  };
20025
+ var PRESET_ALIAS = {
20026
+ official: "official",
20027
+ \uAE30\uC548\uBB38: "official",
20028
+ \uC2DC\uD589\uBB38: "official",
20029
+ \uACF5\uBB38: "official",
20030
+ \uACF5\uBB38\uC11C: "official",
20031
+ report: "report",
20032
+ \uBCF4\uACE0\uC11C: "report",
20033
+ plan: "plan",
20034
+ \uACC4\uD68D\uC11C: "plan",
20035
+ \uACC4\uD68D: "plan",
20036
+ notice: "notice",
20037
+ \uD1B5\uC9C0: "notice",
20038
+ \uC54C\uB9BC: "notice",
20039
+ \uC548\uB0B4: "notice",
20040
+ minutes: "minutes",
20041
+ \uD68C\uC758\uB85D: "minutes"
20042
+ };
20043
+ function normalizeGongmunPreset(preset) {
20044
+ if (!preset) return "official";
20045
+ return PRESET_ALIAS[preset.trim()] ?? "official";
20046
+ }
20013
20047
  function resolveGongmun(opts) {
20014
- const preset = opts.preset ?? "official";
20048
+ const preset = normalizeGongmunPreset(opts.preset);
20015
20049
  const d = PRESET_DEFAULTS[preset];
20016
20050
  const bodyPt = opts.bodyPt ?? d.bodyPt;
20017
20051
  return {
@@ -21381,6 +21415,67 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
21381
21415
  return 1;
21382
21416
  }
21383
21417
 
21418
+ // src/roundtrip/table-insert.ts
21419
+ var TABLE_USABLE_WIDTH = 44e3;
21420
+ var TABLE_ROW_HEIGHT = 1500;
21421
+ function collectMaxNumericId(xmls) {
21422
+ let max = -1;
21423
+ for (const xml of xmls) {
21424
+ for (const m of xml.matchAll(/\bid(?:Ref)?="(\d{1,10})"/g)) {
21425
+ const v = parseInt(m[1], 10);
21426
+ if (!isNaN(v) && v > max) max = v;
21427
+ }
21428
+ }
21429
+ return max;
21430
+ }
21431
+ function cellBorderFillXml(id) {
21432
+ return `<hh:borderFill id="${id}" threeD="0" shadow="0" centerLine="0" breakCellSeparateLine="0"><hh:slash type="NONE" Crooked="0" isCounter="0"/><hh:backSlash type="NONE" Crooked="0" isCounter="0"/><hh:leftBorder type="SOLID" width="0.12 mm" color="#000000"/><hh:rightBorder type="SOLID" width="0.12 mm" color="#000000"/><hh:topBorder type="SOLID" width="0.12 mm" color="#000000"/><hh:bottomBorder type="SOLID" width="0.12 mm" color="#000000"/><hh:diagonal type="NONE" width="0.1 mm" color="#000000"/><hh:fillInfo/></hh:borderFill>`;
21433
+ }
21434
+ function injectCellBorderFill(headerXml, newId) {
21435
+ const openM = headerXml.match(/<hh:borderFills\b([^>]*)>/);
21436
+ if (!openM || openM.index === void 0) return null;
21437
+ const closeIdx = headerXml.indexOf("</hh:borderFills>");
21438
+ if (closeIdx < 0) return null;
21439
+ const splices = [];
21440
+ const itemCntM = openM[1].match(/\bitemCnt="(\d+)"/);
21441
+ if (itemCntM && itemCntM.index !== void 0) {
21442
+ const cnt = parseInt(itemCntM[1], 10);
21443
+ const attrStart = openM.index + "<hh:borderFills".length + itemCntM.index;
21444
+ const valStart = attrStart + itemCntM[0].indexOf('"') + 1;
21445
+ const valEnd = valStart + String(cnt).length;
21446
+ splices.push({ start: valStart, end: valEnd, replacement: String(cnt + 1) });
21447
+ }
21448
+ splices.push({ start: closeIdx, end: closeIdx, replacement: cellBorderFillXml(newId) });
21449
+ return { borderFillId: newId, headerSplices: splices };
21450
+ }
21451
+ function cellPlainText(raw) {
21452
+ let t = unescapeGfmCell(raw);
21453
+ t = t.replace(/\*\*\*([^*]+)\*\*\*/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1");
21454
+ return t.replace(/\s*\n\s*/g, " ").trim();
21455
+ }
21456
+ function buildTableParagraphXml(rows, opts) {
21457
+ const { borderFillId, outerParaPrId, cellParaPrId, cellCharPrId, tableId } = opts;
21458
+ const rowCnt = rows.length;
21459
+ const colCnt = Math.max(...rows.map((r) => r.length), 1);
21460
+ const cellW = Math.floor(TABLE_USABLE_WIDTH / colCnt);
21461
+ const cellH = TABLE_ROW_HEIGHT;
21462
+ const tblW = cellW * colCnt;
21463
+ const tblH = cellH * rowCnt;
21464
+ const trElements = rows.map((row, rowIdx) => {
21465
+ const cells = row.length < colCnt ? [...row, ...Array(colCnt - row.length).fill("")] : row;
21466
+ const isHeaderRow = rowIdx === 0;
21467
+ const tdElements = cells.map((cell, colIdx) => {
21468
+ const text = escapeXmlText(cellPlainText(cell));
21469
+ const cellP = `<hp:p paraPrIDRef="${cellParaPrId}" styleIDRef="0"><hp:run charPrIDRef="${cellCharPrId}"><hp:t>${text}</hp:t></hp:run></hp:p>`;
21470
+ return `<hp:tc name="" header="${isHeaderRow ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="${borderFillId}"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${cellP}</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>`;
21471
+ }).join("");
21472
+ return `<hp:tr>${tdElements}</hp:tr>`;
21473
+ }).join("");
21474
+ const tblInner = `<hp:sz width="${tblW}" widthRelTo="ABSOLUTE" height="${tblH}" heightRelTo="ABSOLUTE" protect="0"/><hp:pos treatAsChar="1" affectLSpacing="0" flowWithText="0" allowOverlap="0" holdAnchorAndSO="0" vertRelTo="PARA" horzRelTo="PARA" vertAlign="TOP" horzAlign="LEFT" vertOffset="0" horzOffset="0"/><hp:outMargin left="0" right="0" top="0" bottom="0"/><hp:inMargin left="510" right="510" top="141" bottom="141"/>` + trElements;
21475
+ const tbl = `<hp:tbl id="${tableId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="0" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="${borderFillId}" noShading="0">${tblInner}</hp:tbl>`;
21476
+ return `<hp:p paraPrIDRef="${outerParaPrId}" styleIDRef="0"><hp:run charPrIDRef="${cellCharPrId}">${tbl}</hp:run></hp:p>`;
21477
+ }
21478
+
21384
21479
  // src/roundtrip/hwpx-entries.ts
21385
21480
  async function resolveSectionEntryNames(zip) {
21386
21481
  for (const mp of ["Contents/content.hpf", "content.hpf"]) {
@@ -21390,10 +21485,9 @@ async function resolveSectionEntryNames(zip) {
21390
21485
  const paths = sectionPathsFromManifest(xml).filter((p) => zip.file(p) !== null);
21391
21486
  if (paths.length > 0) return paths;
21392
21487
  }
21393
- return Object.keys(zip.files).filter((n) => /[Ss]ection\d+\.xml$/.test(n)).sort();
21488
+ return Object.keys(zip.files).filter((n) => /[Ss]ection\d+\.xml$/.test(n)).sort(compareSectionPaths);
21394
21489
  }
21395
21490
  function sectionPathsFromManifest(xml) {
21396
- const isSectionId = (id) => /^s/i.test(id) || id.toLowerCase().includes("section");
21397
21491
  const attr = (tag, name) => {
21398
21492
  const m = tag.match(new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`));
21399
21493
  return m ? m[1] ?? m[2] : "";
@@ -21401,11 +21495,8 @@ function sectionPathsFromManifest(xml) {
21401
21495
  const idToHref = /* @__PURE__ */ new Map();
21402
21496
  for (const m of xml.matchAll(/<opf:item(\s(?:"[^"]*"|'[^']*'|[^>"'])*?)\/?>/g)) {
21403
21497
  const id = attr(m[1], "id");
21404
- let href = attr(m[1], "href");
21405
- const mediaType = attr(m[1], "media-type");
21406
- if (!isSectionId(id) && !mediaType.includes("xml")) continue;
21407
- if (!href.startsWith("/") && !href.startsWith("Contents/") && isSectionId(id)) href = "Contents/" + href;
21408
- if (id) idToHref.set(id, href);
21498
+ const href = normalizeSectionHref(attr(m[1], "href"));
21499
+ if (id && href) idToHref.set(id, href);
21409
21500
  }
21410
21501
  const ordered = [];
21411
21502
  for (const m of xml.matchAll(/<opf:itemref(\s(?:"[^"]*"|'[^']*'|[^>"'])*?)\/?>/g)) {
@@ -21413,7 +21504,7 @@ function sectionPathsFromManifest(xml) {
21413
21504
  if (href) ordered.push(href);
21414
21505
  }
21415
21506
  if (ordered.length > 0) return ordered;
21416
- return Array.from(idToHref.entries()).filter(([id]) => isSectionId(id)).sort((a, b) => a[0].localeCompare(b[0])).map(([, href]) => href);
21507
+ return Array.from(idToHref.values()).sort(compareSectionPaths);
21417
21508
  }
21418
21509
 
21419
21510
  // src/roundtrip/patcher.ts
@@ -21442,6 +21533,13 @@ async function patchHwpx(original, editedMarkdown, options) {
21442
21533
  const xml = await zip.file(sectionPaths[i]).async("text");
21443
21534
  scans.push(scanSectionXml(xml, i));
21444
21535
  }
21536
+ let tableInsert;
21537
+ const headerEntryName = await resolveHeaderEntryName(zip);
21538
+ if (headerEntryName) {
21539
+ const headerXml = await zip.file(headerEntryName).async("text");
21540
+ const maxId = collectMaxNumericId([...scans.map((s) => s.xml), headerXml]);
21541
+ tableInsert = { headerEntryName, headerXml, headerSplices: [], borderFillId: null, nextId: maxId + 1 };
21542
+ }
21445
21543
  const origUnits = buildOrigUnits(origBlocks);
21446
21544
  const editedUnits = splitMarkdownUnits(editedMarkdown);
21447
21545
  const pairs = alignUnits(origUnits.map((u) => u.raw), editedUnits.map((u) => u.raw));
@@ -21461,7 +21559,8 @@ async function patchHwpx(original, editedMarkdown, options) {
21461
21559
  scanTables,
21462
21560
  obTableOrdinals,
21463
21561
  sectionSplices,
21464
- skipped
21562
+ skipped,
21563
+ tableInsert
21465
21564
  });
21466
21565
  } else if (oi !== null) {
21467
21566
  skipped.push({ reason: "\uBE14\uB85D \uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0 (v1) \u2014 \uC6D0\uBCF8 \uC720\uC9C0", before: summarize(origUnits[oi].raw) });
@@ -21478,6 +21577,10 @@ async function patchHwpx(original, editedMarkdown, options) {
21478
21577
  const newXml = applySplices(scans[i].xml, sectionSplices[i]);
21479
21578
  replacements.set(sectionPaths[i], encoder.encode(newXml));
21480
21579
  }
21580
+ if (tableInsert && tableInsert.headerSplices.length > 0) {
21581
+ const newHeader = applySplices(tableInsert.headerXml, tableInsert.headerSplices);
21582
+ replacements.set(tableInsert.headerEntryName, encoder.encode(newHeader));
21583
+ }
21481
21584
  } catch (err) {
21482
21585
  return { success: false, applied: 0, skipped, error: `\uC18C\uC2A4\uB9F5 splice \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}` };
21483
21586
  }
@@ -21690,11 +21793,49 @@ function handleModifiedUnit(orig, edited, ctx) {
21690
21793
  if (orig.kind === "html-table") return patchHtmlTable(block.table, scanTable, orig, edited, ctx, skip);
21691
21794
  return patchTextChunkTable(block.table, scanTable, orig, edited, ctx, skip);
21692
21795
  }
21693
- if ((block.type === "paragraph" || block.type === "heading") && orig.kind === "text" && edited.kind === "text") {
21694
- return patchParagraphUnit(block, orig, edited, ctx, skip);
21796
+ if ((block.type === "paragraph" || block.type === "heading") && orig.kind === "text") {
21797
+ if (edited.kind === "text") return patchParagraphUnit(block, orig, edited, ctx, skip);
21798
+ if (edited.kind === "gfm-table") return convertParagraphToTable(block, orig, edited, ctx, skip);
21799
+ if (edited.kind === "html-table") return skip("\uBB38\uB2E8\u2192\uBCD1\uD569\uD45C(HTML) \uBCC0\uD658\uC740 \uBBF8\uC9C0\uC6D0 \u2014 GFM \uD45C(| \uD5E4\uB354 | \u2026 |)\uB85C \uC791\uC131\uD558\uC138\uC694");
21695
21800
  }
21696
21801
  return skip("\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBE14\uB85D \uC720\uD615 \uBCC0\uACBD");
21697
21802
  }
21803
+ function extractParaPrIdRef(xml, start) {
21804
+ const gt = xml.indexOf(">", start);
21805
+ if (gt < 0) return null;
21806
+ const m = xml.slice(start, gt + 1).match(/paraPrIDRef="(\d+)"/);
21807
+ return m ? parseInt(m[1], 10) : null;
21808
+ }
21809
+ function convertParagraphToTable(block, orig, edited, ctx, skip) {
21810
+ const ti = ctx.tableInsert;
21811
+ if (!ti) return skip("\uBB38\uB2E8\u2192\uD45C \uBCC0\uD658 \uBD88\uAC00 \u2014 header \uC5D4\uD2B8\uB9AC(borderFills) \uD574\uC11D \uC2E4\uD328");
21812
+ const mapping = ctx.paraMap.get(orig.blockIdx);
21813
+ if (!mapping?.para) return skip("\uBB38\uB2E8 \uC18C\uC2A4\uB9F5 \uB9E4\uD551 \uC2E4\uD328 (\uBA38\uB9AC\uB9D0/\uAE00\uC0C1\uC790/\uCEA1\uC158 \uC601\uC5ED) \u2014 \uD45C \uBCC0\uD658 \uBD88\uAC00");
21814
+ const para = mapping.para;
21815
+ if (para.kind !== "body") return skip("\uBCF8\uBB38 \uC678 \uC601\uC5ED(\uD45C \uC140/\uAE00\uC0C1\uC790) \uBB38\uB2E8\uC758 \uD45C \uBCC0\uD658\uC740 \uBBF8\uC9C0\uC6D0");
21816
+ const scan = ctx.scans[para.sectionIndex];
21817
+ if (!scan) return skip("\uC139\uC158 \uB9E4\uD551 \uC2E4\uD328");
21818
+ const pEnd = findElementEnd(scan.xml, para.start);
21819
+ if (pEnd < 0) return skip("\uBB38\uB2E8 \uB05D \uC704\uCE58 \uD0D0\uC0C9 \uC2E4\uD328");
21820
+ const rows = parseGfmTable(edited.lines);
21821
+ if (rows.length === 0 || rows.every((r) => r.length === 0)) return skip("\uD45C \uB0B4\uC6A9\uC774 \uBE44\uC5B4 \uC788\uC74C");
21822
+ if (ti.borderFillId === null) {
21823
+ const inj = injectCellBorderFill(ti.headerXml, ti.nextId++);
21824
+ if (!inj) return skip("header <hh:borderFills> \uAD6C\uC870\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC5B4 \uD45C \uD14C\uB450\uB9AC \uC0DD\uC131 \uBD88\uAC00");
21825
+ ti.borderFillId = inj.borderFillId;
21826
+ ti.headerSplices.push(...inj.headerSplices);
21827
+ }
21828
+ const outerParaPrId = extractParaPrIdRef(scan.xml, para.start) ?? 0;
21829
+ const tableXml = buildTableParagraphXml(rows, {
21830
+ borderFillId: ti.borderFillId,
21831
+ outerParaPrId,
21832
+ cellParaPrId: 0,
21833
+ cellCharPrId: 0,
21834
+ tableId: ti.nextId++
21835
+ });
21836
+ ctx.sectionSplices[para.sectionIndex].push({ start: para.start, end: pEnd, replacement: tableXml });
21837
+ return 1;
21838
+ }
21698
21839
  function patchParagraphUnit(block, orig, edited, ctx, skip) {
21699
21840
  const mapping = ctx.paraMap.get(orig.blockIdx);
21700
21841
  if (!mapping?.para) return skip("\uBB38\uB2E8 \uC18C\uC2A4\uB9F5 \uB9E4\uD551 \uC2E4\uD328 (\uBA38\uB9AC\uB9D0/\uAE00\uC0C1\uC790/\uCEA1\uC158 \uC601\uC5ED\uC774\uAC70\uB098 \uD14D\uC2A4\uD2B8 \uBD88\uC77C\uCE58)");
@@ -21775,6 +21916,24 @@ function unitToBlock(u) {
21775
21916
  function u8ToArrayBuffer(u8) {
21776
21917
  return u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
21777
21918
  }
21919
+ async function resolveHeaderEntryName(zip) {
21920
+ for (const p of ["Contents/header.xml", "header.xml"]) {
21921
+ if (zip.file(p)) return p;
21922
+ }
21923
+ for (const mp of ["Contents/content.hpf", "content.hpf"]) {
21924
+ const f = zip.file(mp);
21925
+ if (!f) continue;
21926
+ const xml = await f.async("text");
21927
+ const m = xml.match(/<opf:item\b[^>]*\bid="header"[^>]*\bhref="([^"]+)"/i) || xml.match(/<opf:item\b[^>]*\bhref="([^"]*header[^"]*\.xml)"/i);
21928
+ if (m) {
21929
+ let href = m[1];
21930
+ if (!href.startsWith("/") && !href.startsWith("Contents/")) href = "Contents/" + href;
21931
+ if (zip.file(href)) return href;
21932
+ }
21933
+ }
21934
+ const found = Object.keys(zip.files).find((n) => /header\.xml$/i.test(n));
21935
+ return found ?? null;
21936
+ }
21778
21937
 
21779
21938
  // src/roundtrip/hwp5-patch.ts
21780
21939
  import { deflateRawSync as deflateRawSync2 } from "zlib";
@@ -22413,8 +22572,11 @@ function handleModified(orig, edited, ctx) {
22413
22572
  if (orig.kind === "html-table") return patchHtmlCells5(block.table, scanTable, orig, edited, ctx, skip);
22414
22573
  return patchTextChunk5(block.table, scanTable, orig, edited, ctx, skip);
22415
22574
  }
22416
- if ((block.type === "paragraph" || block.type === "heading") && orig.kind === "text" && edited.kind === "text") {
22417
- return patchParagraph(block, orig, edited, ctx, skip);
22575
+ if ((block.type === "paragraph" || block.type === "heading") && orig.kind === "text") {
22576
+ if (edited.kind === "text") return patchParagraph(block, orig, edited, ctx, skip);
22577
+ if (edited.kind === "gfm-table" || edited.kind === "html-table") {
22578
+ return skip("HWP5(.hwp) \uBC14\uC774\uB108\uB9AC\uB294 \uBB38\uB2E8\u2192\uD45C \uC778\uD50C\uB808\uC774\uC2A4 \uBCC0\uD658 \uBBF8\uC9C0\uC6D0 \u2014 generate\uB85C \uC0C8 \uBB38\uC11C\uB97C \uB9CC\uB4E4\uAC70\uB098, HWPX(.hwpx)\uB85C \uC800\uC7A5 \uD6C4 patch\uD558\uC138\uC694");
22579
+ }
22418
22580
  }
22419
22581
  return skip("\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBE14\uB85D \uC720\uD615 \uBCC0\uACBD");
22420
22582
  }
@@ -23351,7 +23513,7 @@ async function parseHwp(buffer, options) {
23351
23513
  async function parsePdf(buffer, options) {
23352
23514
  let parsePdfDocument;
23353
23515
  try {
23354
- const mod = await import("./parser-5HDZZWDH.js");
23516
+ const mod = await import("./parser-GHZMDUGG.js");
23355
23517
  parsePdfDocument = mod.parsePdfDocument;
23356
23518
  } catch {
23357
23519
  return {
@@ -23442,6 +23604,7 @@ async function fillForm(input, values, outputFormat = "markdown") {
23442
23604
  }
23443
23605
  export {
23444
23606
  HwpxSession,
23607
+ PRESET_ALIAS,
23445
23608
  VERSION,
23446
23609
  applySplices,
23447
23610
  blocksToMarkdown,
@@ -23466,6 +23629,7 @@ export {
23466
23629
  isZipFile,
23467
23630
  markdownToHwpx,
23468
23631
  markdownToPdf,
23632
+ normalizeGongmunPreset,
23469
23633
  openHwpxDocument,
23470
23634
  parse,
23471
23635
  parseDocx,