kordoc 3.4.1 → 3.5.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 (31) hide show
  1. package/README.md +6 -0
  2. package/dist/{-YOQGQ22S.js → -GKNSTVEZ.js} +7 -3
  3. package/dist/{chunk-W7JHJQ2O.cjs → chunk-4JLD2IT7.cjs} +2 -2
  4. package/dist/{chunk-W7JHJQ2O.cjs.map → chunk-4JLD2IT7.cjs.map} +1 -1
  5. package/dist/{chunk-FPFB7AFX.js → chunk-A4LSWJEC.js} +2 -2
  6. package/dist/{chunk-SE3KEL4G.js → chunk-AHDNELBR.js} +2 -2
  7. package/dist/{chunk-CGHPBI3X.js → chunk-GTCJQANY.js} +165 -9
  8. package/dist/chunk-GTCJQANY.js.map +1 -0
  9. package/dist/cli.js +6 -23
  10. package/dist/cli.js.map +1 -1
  11. package/dist/index.cjs +302 -146
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +9 -3
  14. package/dist/index.d.ts +9 -3
  15. package/dist/index.js +164 -8
  16. package/dist/index.js.map +1 -1
  17. package/dist/mcp.js +39 -3
  18. package/dist/mcp.js.map +1 -1
  19. package/dist/{parser-JAZCZJI6.cjs → parser-25ISHR7Q.cjs} +14 -14
  20. package/dist/{parser-JAZCZJI6.cjs.map → parser-25ISHR7Q.cjs.map} +1 -1
  21. package/dist/{parser-5HDZZWDH.js → parser-LBLPN3KY.js} +2 -2
  22. package/dist/{parser-ET64VEEX.js → parser-LMBDB7RM.js} +2 -2
  23. package/dist/{watch-56GNC2YN.js → watch-77CYT54V.js} +3 -3
  24. package/package.json +1 -1
  25. package/dist/chunk-CGHPBI3X.js.map +0 -1
  26. /package/dist/{-YOQGQ22S.js.map → -GKNSTVEZ.js.map} +0 -0
  27. /package/dist/{chunk-FPFB7AFX.js.map → chunk-A4LSWJEC.js.map} +0 -0
  28. /package/dist/{chunk-SE3KEL4G.js.map → chunk-AHDNELBR.js.map} +0 -0
  29. /package/dist/{parser-5HDZZWDH.js.map → parser-LBLPN3KY.js.map} +0 -0
  30. /package/dist/{parser-ET64VEEX.js.map → parser-LMBDB7RM.js.map} +0 -0
  31. /package/dist/{watch-56GNC2YN.js.map → watch-77CYT54V.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
@@ -17,7 +17,7 @@ import {
17
17
  sanitizeHref,
18
18
  stripDtd,
19
19
  toArrayBuffer
20
- } from "./chunk-SE3KEL4G.js";
20
+ } from "./chunk-AHDNELBR.js";
21
21
  import {
22
22
  parsePageRange
23
23
  } from "./chunk-AIQ3ISQU.js";
@@ -20010,8 +20010,30 @@ var PRESET_DEFAULTS = {
20010
20010
  notice: { bodyPt: 15, lineSpacing: 160, numbering: "standard" },
20011
20011
  minutes: { bodyPt: 14, lineSpacing: 130, numbering: "standard" }
20012
20012
  };
20013
+ var PRESET_ALIAS = {
20014
+ official: "official",
20015
+ \uAE30\uC548\uBB38: "official",
20016
+ \uC2DC\uD589\uBB38: "official",
20017
+ \uACF5\uBB38: "official",
20018
+ \uACF5\uBB38\uC11C: "official",
20019
+ report: "report",
20020
+ \uBCF4\uACE0\uC11C: "report",
20021
+ plan: "plan",
20022
+ \uACC4\uD68D\uC11C: "plan",
20023
+ \uACC4\uD68D: "plan",
20024
+ notice: "notice",
20025
+ \uD1B5\uC9C0: "notice",
20026
+ \uC54C\uB9BC: "notice",
20027
+ \uC548\uB0B4: "notice",
20028
+ minutes: "minutes",
20029
+ \uD68C\uC758\uB85D: "minutes"
20030
+ };
20031
+ function normalizeGongmunPreset(preset) {
20032
+ if (!preset) return "official";
20033
+ return PRESET_ALIAS[preset.trim()] ?? "official";
20034
+ }
20013
20035
  function resolveGongmun(opts) {
20014
- const preset = opts.preset ?? "official";
20036
+ const preset = normalizeGongmunPreset(opts.preset);
20015
20037
  const d = PRESET_DEFAULTS[preset];
20016
20038
  const bodyPt = opts.bodyPt ?? d.bodyPt;
20017
20039
  return {
@@ -21381,6 +21403,67 @@ function applyCellEdit(table, scanTable, gridR, gridC, newLines, ctx, before, af
21381
21403
  return 1;
21382
21404
  }
21383
21405
 
21406
+ // src/roundtrip/table-insert.ts
21407
+ var TABLE_USABLE_WIDTH = 44e3;
21408
+ var TABLE_ROW_HEIGHT = 1500;
21409
+ function collectMaxNumericId(xmls) {
21410
+ let max = -1;
21411
+ for (const xml of xmls) {
21412
+ for (const m of xml.matchAll(/\bid(?:Ref)?="(\d{1,10})"/g)) {
21413
+ const v = parseInt(m[1], 10);
21414
+ if (!isNaN(v) && v > max) max = v;
21415
+ }
21416
+ }
21417
+ return max;
21418
+ }
21419
+ function cellBorderFillXml(id) {
21420
+ 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>`;
21421
+ }
21422
+ function injectCellBorderFill(headerXml, newId) {
21423
+ const openM = headerXml.match(/<hh:borderFills\b([^>]*)>/);
21424
+ if (!openM || openM.index === void 0) return null;
21425
+ const closeIdx = headerXml.indexOf("</hh:borderFills>");
21426
+ if (closeIdx < 0) return null;
21427
+ const splices = [];
21428
+ const itemCntM = openM[1].match(/\bitemCnt="(\d+)"/);
21429
+ if (itemCntM && itemCntM.index !== void 0) {
21430
+ const cnt = parseInt(itemCntM[1], 10);
21431
+ const attrStart = openM.index + "<hh:borderFills".length + itemCntM.index;
21432
+ const valStart = attrStart + itemCntM[0].indexOf('"') + 1;
21433
+ const valEnd = valStart + String(cnt).length;
21434
+ splices.push({ start: valStart, end: valEnd, replacement: String(cnt + 1) });
21435
+ }
21436
+ splices.push({ start: closeIdx, end: closeIdx, replacement: cellBorderFillXml(newId) });
21437
+ return { borderFillId: newId, headerSplices: splices };
21438
+ }
21439
+ function cellPlainText(raw) {
21440
+ let t = unescapeGfmCell(raw);
21441
+ t = t.replace(/\*\*\*([^*]+)\*\*\*/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1");
21442
+ return t.replace(/\s*\n\s*/g, " ").trim();
21443
+ }
21444
+ function buildTableParagraphXml(rows, opts) {
21445
+ const { borderFillId, outerParaPrId, cellParaPrId, cellCharPrId, tableId } = opts;
21446
+ const rowCnt = rows.length;
21447
+ const colCnt = Math.max(...rows.map((r) => r.length), 1);
21448
+ const cellW = Math.floor(TABLE_USABLE_WIDTH / colCnt);
21449
+ const cellH = TABLE_ROW_HEIGHT;
21450
+ const tblW = cellW * colCnt;
21451
+ const tblH = cellH * rowCnt;
21452
+ const trElements = rows.map((row, rowIdx) => {
21453
+ const cells = row.length < colCnt ? [...row, ...Array(colCnt - row.length).fill("")] : row;
21454
+ const isHeaderRow = rowIdx === 0;
21455
+ const tdElements = cells.map((cell, colIdx) => {
21456
+ const text = escapeXmlText(cellPlainText(cell));
21457
+ const cellP = `<hp:p paraPrIDRef="${cellParaPrId}" styleIDRef="0"><hp:run charPrIDRef="${cellCharPrId}"><hp:t>${text}</hp:t></hp:run></hp:p>`;
21458
+ 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>`;
21459
+ }).join("");
21460
+ return `<hp:tr>${tdElements}</hp:tr>`;
21461
+ }).join("");
21462
+ 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;
21463
+ 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>`;
21464
+ return `<hp:p paraPrIDRef="${outerParaPrId}" styleIDRef="0"><hp:run charPrIDRef="${cellCharPrId}">${tbl}</hp:run></hp:p>`;
21465
+ }
21466
+
21384
21467
  // src/roundtrip/hwpx-entries.ts
21385
21468
  async function resolveSectionEntryNames(zip) {
21386
21469
  for (const mp of ["Contents/content.hpf", "content.hpf"]) {
@@ -21442,6 +21525,13 @@ async function patchHwpx(original, editedMarkdown, options) {
21442
21525
  const xml = await zip.file(sectionPaths[i]).async("text");
21443
21526
  scans.push(scanSectionXml(xml, i));
21444
21527
  }
21528
+ let tableInsert;
21529
+ const headerEntryName = await resolveHeaderEntryName(zip);
21530
+ if (headerEntryName) {
21531
+ const headerXml = await zip.file(headerEntryName).async("text");
21532
+ const maxId = collectMaxNumericId([...scans.map((s) => s.xml), headerXml]);
21533
+ tableInsert = { headerEntryName, headerXml, headerSplices: [], borderFillId: null, nextId: maxId + 1 };
21534
+ }
21445
21535
  const origUnits = buildOrigUnits(origBlocks);
21446
21536
  const editedUnits = splitMarkdownUnits(editedMarkdown);
21447
21537
  const pairs = alignUnits(origUnits.map((u) => u.raw), editedUnits.map((u) => u.raw));
@@ -21461,7 +21551,8 @@ async function patchHwpx(original, editedMarkdown, options) {
21461
21551
  scanTables,
21462
21552
  obTableOrdinals,
21463
21553
  sectionSplices,
21464
- skipped
21554
+ skipped,
21555
+ tableInsert
21465
21556
  });
21466
21557
  } else if (oi !== null) {
21467
21558
  skipped.push({ reason: "\uBE14\uB85D \uC0AD\uC81C\uB294 \uBBF8\uC9C0\uC6D0 (v1) \u2014 \uC6D0\uBCF8 \uC720\uC9C0", before: summarize(origUnits[oi].raw) });
@@ -21478,6 +21569,10 @@ async function patchHwpx(original, editedMarkdown, options) {
21478
21569
  const newXml = applySplices(scans[i].xml, sectionSplices[i]);
21479
21570
  replacements.set(sectionPaths[i], encoder.encode(newXml));
21480
21571
  }
21572
+ if (tableInsert && tableInsert.headerSplices.length > 0) {
21573
+ const newHeader = applySplices(tableInsert.headerXml, tableInsert.headerSplices);
21574
+ replacements.set(tableInsert.headerEntryName, encoder.encode(newHeader));
21575
+ }
21481
21576
  } catch (err) {
21482
21577
  return { success: false, applied: 0, skipped, error: `\uC18C\uC2A4\uB9F5 splice \uC2E4\uD328: ${err instanceof Error ? err.message : String(err)}` };
21483
21578
  }
@@ -21690,11 +21785,49 @@ function handleModifiedUnit(orig, edited, ctx) {
21690
21785
  if (orig.kind === "html-table") return patchHtmlTable(block.table, scanTable, orig, edited, ctx, skip);
21691
21786
  return patchTextChunkTable(block.table, scanTable, orig, edited, ctx, skip);
21692
21787
  }
21693
- if ((block.type === "paragraph" || block.type === "heading") && orig.kind === "text" && edited.kind === "text") {
21694
- return patchParagraphUnit(block, orig, edited, ctx, skip);
21788
+ if ((block.type === "paragraph" || block.type === "heading") && orig.kind === "text") {
21789
+ if (edited.kind === "text") return patchParagraphUnit(block, orig, edited, ctx, skip);
21790
+ if (edited.kind === "gfm-table") return convertParagraphToTable(block, orig, edited, ctx, skip);
21791
+ 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
21792
  }
21696
21793
  return skip("\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBE14\uB85D \uC720\uD615 \uBCC0\uACBD");
21697
21794
  }
21795
+ function extractParaPrIdRef(xml, start) {
21796
+ const gt = xml.indexOf(">", start);
21797
+ if (gt < 0) return null;
21798
+ const m = xml.slice(start, gt + 1).match(/paraPrIDRef="(\d+)"/);
21799
+ return m ? parseInt(m[1], 10) : null;
21800
+ }
21801
+ function convertParagraphToTable(block, orig, edited, ctx, skip) {
21802
+ const ti = ctx.tableInsert;
21803
+ if (!ti) return skip("\uBB38\uB2E8\u2192\uD45C \uBCC0\uD658 \uBD88\uAC00 \u2014 header \uC5D4\uD2B8\uB9AC(borderFills) \uD574\uC11D \uC2E4\uD328");
21804
+ const mapping = ctx.paraMap.get(orig.blockIdx);
21805
+ 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");
21806
+ const para = mapping.para;
21807
+ 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");
21808
+ const scan = ctx.scans[para.sectionIndex];
21809
+ if (!scan) return skip("\uC139\uC158 \uB9E4\uD551 \uC2E4\uD328");
21810
+ const pEnd = findElementEnd(scan.xml, para.start);
21811
+ if (pEnd < 0) return skip("\uBB38\uB2E8 \uB05D \uC704\uCE58 \uD0D0\uC0C9 \uC2E4\uD328");
21812
+ const rows = parseGfmTable(edited.lines);
21813
+ if (rows.length === 0 || rows.every((r) => r.length === 0)) return skip("\uD45C \uB0B4\uC6A9\uC774 \uBE44\uC5B4 \uC788\uC74C");
21814
+ if (ti.borderFillId === null) {
21815
+ const inj = injectCellBorderFill(ti.headerXml, ti.nextId++);
21816
+ if (!inj) return skip("header <hh:borderFills> \uAD6C\uC870\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC5B4 \uD45C \uD14C\uB450\uB9AC \uC0DD\uC131 \uBD88\uAC00");
21817
+ ti.borderFillId = inj.borderFillId;
21818
+ ti.headerSplices.push(...inj.headerSplices);
21819
+ }
21820
+ const outerParaPrId = extractParaPrIdRef(scan.xml, para.start) ?? 0;
21821
+ const tableXml = buildTableParagraphXml(rows, {
21822
+ borderFillId: ti.borderFillId,
21823
+ outerParaPrId,
21824
+ cellParaPrId: 0,
21825
+ cellCharPrId: 0,
21826
+ tableId: ti.nextId++
21827
+ });
21828
+ ctx.sectionSplices[para.sectionIndex].push({ start: para.start, end: pEnd, replacement: tableXml });
21829
+ return 1;
21830
+ }
21698
21831
  function patchParagraphUnit(block, orig, edited, ctx, skip) {
21699
21832
  const mapping = ctx.paraMap.get(orig.blockIdx);
21700
21833
  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 +21908,24 @@ function unitToBlock(u) {
21775
21908
  function u8ToArrayBuffer(u8) {
21776
21909
  return u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
21777
21910
  }
21911
+ async function resolveHeaderEntryName(zip) {
21912
+ for (const p of ["Contents/header.xml", "header.xml"]) {
21913
+ if (zip.file(p)) return p;
21914
+ }
21915
+ for (const mp of ["Contents/content.hpf", "content.hpf"]) {
21916
+ const f = zip.file(mp);
21917
+ if (!f) continue;
21918
+ const xml = await f.async("text");
21919
+ const m = xml.match(/<opf:item\b[^>]*\bid="header"[^>]*\bhref="([^"]+)"/i) || xml.match(/<opf:item\b[^>]*\bhref="([^"]*header[^"]*\.xml)"/i);
21920
+ if (m) {
21921
+ let href = m[1];
21922
+ if (!href.startsWith("/") && !href.startsWith("Contents/")) href = "Contents/" + href;
21923
+ if (zip.file(href)) return href;
21924
+ }
21925
+ }
21926
+ const found = Object.keys(zip.files).find((n) => /header\.xml$/i.test(n));
21927
+ return found ?? null;
21928
+ }
21778
21929
 
21779
21930
  // src/roundtrip/hwp5-patch.ts
21780
21931
  import { deflateRawSync as deflateRawSync2 } from "zlib";
@@ -22413,8 +22564,11 @@ function handleModified(orig, edited, ctx) {
22413
22564
  if (orig.kind === "html-table") return patchHtmlCells5(block.table, scanTable, orig, edited, ctx, skip);
22414
22565
  return patchTextChunk5(block.table, scanTable, orig, edited, ctx, skip);
22415
22566
  }
22416
- if ((block.type === "paragraph" || block.type === "heading") && orig.kind === "text" && edited.kind === "text") {
22417
- return patchParagraph(block, orig, edited, ctx, skip);
22567
+ if ((block.type === "paragraph" || block.type === "heading") && orig.kind === "text") {
22568
+ if (edited.kind === "text") return patchParagraph(block, orig, edited, ctx, skip);
22569
+ if (edited.kind === "gfm-table" || edited.kind === "html-table") {
22570
+ 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");
22571
+ }
22418
22572
  }
22419
22573
  return skip("\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uBE14\uB85D \uC720\uD615 \uBCC0\uACBD");
22420
22574
  }
@@ -23351,7 +23505,7 @@ async function parseHwp(buffer, options) {
23351
23505
  async function parsePdf(buffer, options) {
23352
23506
  let parsePdfDocument;
23353
23507
  try {
23354
- const mod = await import("./parser-5HDZZWDH.js");
23508
+ const mod = await import("./parser-LBLPN3KY.js");
23355
23509
  parsePdfDocument = mod.parsePdfDocument;
23356
23510
  } catch {
23357
23511
  return {
@@ -23442,6 +23596,7 @@ async function fillForm(input, values, outputFormat = "markdown") {
23442
23596
  }
23443
23597
  export {
23444
23598
  HwpxSession,
23599
+ PRESET_ALIAS,
23445
23600
  VERSION,
23446
23601
  applySplices,
23447
23602
  blocksToMarkdown,
@@ -23466,6 +23621,7 @@ export {
23466
23621
  isZipFile,
23467
23622
  markdownToHwpx,
23468
23623
  markdownToPdf,
23624
+ normalizeGongmunPreset,
23469
23625
  openHwpxDocument,
23470
23626
  parse,
23471
23627
  parseDocx,