kordoc 4.0.0 → 4.0.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 (44) hide show
  1. package/dist/{-URVKBXYK.js → -O3AWNVPH.js} +11 -7
  2. package/dist/{chunk-7ETIIYGI.js → chunk-33OUI7HC.js} +2 -2
  3. package/dist/{chunk-IA5UZZKV.js → chunk-3H3CVOZ3.js} +2 -2
  4. package/dist/{chunk-JUJEKFUF.js → chunk-C5ABJ7PB.js} +3 -3
  5. package/dist/{chunk-ZKG6SBHV.js → chunk-EH2KYFFO.js} +2 -2
  6. package/dist/{chunk-THZVBYHE.js → chunk-JMLK6NTW.js} +3 -3
  7. package/dist/{chunk-EBSCV3JC.cjs → chunk-NIZXIVEI.cjs} +2 -2
  8. package/dist/{chunk-EBSCV3JC.cjs.map → chunk-NIZXIVEI.cjs.map} +1 -1
  9. package/dist/{chunk-PGWAF755.js → chunk-SHVV3BD6.js} +200 -40
  10. package/dist/chunk-SHVV3BD6.js.map +1 -0
  11. package/dist/{chunk-DVUNWK6W.js → chunk-YEC7VG6O.js} +2 -2
  12. package/dist/cli.js +49 -13
  13. package/dist/cli.js.map +1 -1
  14. package/dist/index.cjs +446 -286
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +38 -4
  17. package/dist/index.d.ts +38 -4
  18. package/dist/index.js +195 -35
  19. package/dist/index.js.map +1 -1
  20. package/dist/mcp.js +17 -13
  21. package/dist/mcp.js.map +1 -1
  22. package/dist/{parser-6TV6JKHF.cjs → parser-44OPXZMP.cjs} +14 -14
  23. package/dist/{parser-6TV6JKHF.cjs.map → parser-44OPXZMP.cjs.map} +1 -1
  24. package/dist/{parser-NXHY37WH.js → parser-G433UZMX.js} +2 -2
  25. package/dist/{parser-LHUO6UUA.js → parser-ZOMDHCA7.js} +3 -3
  26. package/dist/render-SQVQBORJ.js +10 -0
  27. package/dist/seal-6E26L45Z.js +10 -0
  28. package/dist/{watch-PNXTAWDR.js → watch-UYT3ZCBM.js} +7 -7
  29. package/package.json +1 -1
  30. package/dist/chunk-PGWAF755.js.map +0 -1
  31. package/dist/render-T5O6QHBJ.js +0 -10
  32. package/dist/seal-7KNR5WFH.js +0 -10
  33. /package/dist/{-URVKBXYK.js.map → -O3AWNVPH.js.map} +0 -0
  34. /package/dist/{chunk-7ETIIYGI.js.map → chunk-33OUI7HC.js.map} +0 -0
  35. /package/dist/{chunk-IA5UZZKV.js.map → chunk-3H3CVOZ3.js.map} +0 -0
  36. /package/dist/{chunk-JUJEKFUF.js.map → chunk-C5ABJ7PB.js.map} +0 -0
  37. /package/dist/{chunk-ZKG6SBHV.js.map → chunk-EH2KYFFO.js.map} +0 -0
  38. /package/dist/{chunk-THZVBYHE.js.map → chunk-JMLK6NTW.js.map} +0 -0
  39. /package/dist/{chunk-DVUNWK6W.js.map → chunk-YEC7VG6O.js.map} +0 -0
  40. /package/dist/{parser-NXHY37WH.js.map → parser-G433UZMX.js.map} +0 -0
  41. /package/dist/{parser-LHUO6UUA.js.map → parser-ZOMDHCA7.js.map} +0 -0
  42. /package/dist/{render-T5O6QHBJ.js.map → render-SQVQBORJ.js.map} +0 -0
  43. /package/dist/{seal-7KNR5WFH.js.map → seal-6E26L45Z.js.map} +0 -0
  44. /package/dist/{watch-PNXTAWDR.js.map → watch-UYT3ZCBM.js.map} +0 -0
package/dist/index.d.cts CHANGED
@@ -619,7 +619,7 @@ interface GongmunOptions {
619
619
  bodyPt?: number;
620
620
  /** 본문 줄간격(%). 기본 160 (회의록 130) */
621
621
  lineSpacing?: number;
622
- /** 항목부호 체계. 'standard'=법정 8단계(1. 가. 1) …) / 'report'=보고서 불릿(□ ○ - ㆍ) / 'gaejosik'=개조식(□ ○ ㆍ + 부호별 폰트) */
622
+ /** 항목부호 체계. 'standard'=법정 8단계(1. 가. 1) …) / 'report'=보고서 불릿(□ ○ - ㆍ) / 'gaejosik'=개조식(□ ○ - ㆍ + 부호별 폰트) */
623
623
  numbering?: GongmunNumbering;
624
624
  /**
625
625
  * 표지 페이지(개조식 프리셋 기본 켜짐) — 첫 h1을 제목으로, 파랑 장식 바 + 날짜 + 기관명.
@@ -650,8 +650,9 @@ interface GongmunOptions {
650
650
  };
651
651
  /**
652
652
  * 요소별 글꼴 오버라이드 — 기관 표준 폰트 적용이나 미설치 폰트 대체용.
653
- * body=본문(개조식 ○·―) / heading=제목 계열(□·장헤더·표지·목차) / ref=※ 참고 / table=표 셀.
654
- * 개조식 외 프리셋은 body만 적용된다 (bodyFont보다 우선).
653
+ * body=본문(개조식 ○·-) / heading=제목 계열(□·장헤더·표지·목차) / ref=※ 참고 / table=표 셀.
654
+ * 실측 폰트 프리셋(개조식·보고서·계획서)은 네 역할 전부, 그 외 프리셋은 body만
655
+ * 적용된다 (bodyFont보다 우선).
655
656
  */
656
657
  fonts?: {
657
658
  body?: string;
@@ -669,6 +670,12 @@ interface GongmunOptions {
669
670
  approval?: string[];
670
671
  /** 본문 첫 페이지 제목 박스(개조식) — 목차 뒤 본문 시작에 제목 반복(실측 관행). 기본: 표지 있으면 켜짐 */
671
672
  bodyTitleBox?: boolean;
673
+ /**
674
+ * h2 섹션 제목 말머리 (비개조식 — 개조식 h2는 로마숫자 장 헤더가 대체).
675
+ * 'box'=□ 대항목(실측 보고서 양식 관행) / 'number'=아라비아 번호(1. 2. — 공고문 관행)
676
+ * / 'none'=말머리 없음. 기본: 보고서·계획서 'box', 그 외 'none'.
677
+ */
678
+ h2Marker?: "box" | "number" | "none";
672
679
  }
673
680
  /** 프리셋 별칭(한글/영문) → 내부 preset 키. CLI·라이브러리 공용 */
674
681
  declare const PRESET_ALIAS: Record<string, GongmunPreset>;
@@ -852,6 +859,33 @@ declare function isKnownFont(name: string): boolean;
852
859
  */
853
860
  declare function unknownFontWarnings(fonts: Record<string, string | undefined>): string[];
854
861
 
862
+ /**
863
+ * 공문서 표기법 검수기 — 「행정업무의 운영 및 혁신에 관한 규정」 시행규칙 및
864
+ * 행정안전부 행정업무운영 편람의 날짜·시간·금액·기호 표기법을 정규식으로 검사.
865
+ *
866
+ * 원전: jkf87/hwpx-skill gonmun_lint.py(2025 편람 기준 13룰)를 kordoc에 맞게 이식
867
+ * (v4.0.1). URL 쌍점 오탐 가드 등 일부 보강. 검사는 조언용이다 — 생성은 막지 않고
868
+ * 경고만 낸다 (A2 폰트경고와 같은 원칙). 별도 CLI `kordoc lint`는 error 시 exit 1.
869
+ */
870
+ interface GongmunLintFinding {
871
+ /** 1-based 줄 번호 */
872
+ line: number;
873
+ /** 걸린 원문 조각 */
874
+ match: string;
875
+ /** 규칙 코드 (DATE_NO_SPACE 등) */
876
+ rule: string;
877
+ severity: "error" | "warning";
878
+ message: string;
879
+ suggest?: string;
880
+ }
881
+ /**
882
+ * 텍스트(마크다운 포함) 표기법 검수. 마크다운 펜스 코드블록(``` ~ ```) 안은
883
+ * 건너뛴다 — 코드·URL이 날짜/쌍점 규칙에 오탐되는 것 방지.
884
+ */
885
+ declare function lintGongmunText(text: string): GongmunLintFinding[];
886
+ /** 검수 결과를 사람이 읽는 경고 문자열 배열로 — generate 경고 채널(A2와 동일)용 */
887
+ declare function gongmunLintWarnings(text: string, limit?: number): string[];
888
+
855
889
  /**
856
890
  * 한글 조판 텍스트 폭 계산 — 함초롬바탕(HCR Batang) 실측 advance 테이블
857
891
  *
@@ -1416,4 +1450,4 @@ interface FillFormOutput {
1416
1450
  */
1417
1451
  declare function fillForm(input: string | ArrayBuffer | Buffer, values: Record<string, FillInput>, outputFormat?: FillOutputFormat): Promise<FillFormOutput>;
1418
1452
 
1419
- export { type BlockCapability, type BlockCapabilityInfo, type BlockDiff, type BlockEdit, type BlockSourceRef, type BorderDef, type BorderFillDef, type BoundingBox, type CellCapability, type CellContext, type CellDiff, type CellProfile, type CharPrDef, type DiffChangeType, type DiffResult, type DocumentMetadata, type DocumentQualitySummary, type ErrorCode, type ExtractedImage, type FileType, type FillFormOutput, type FillInput, type FillOutputFormat, type FillResult, type FillValue, type FormField, type FormFieldSchema, type FormFieldType, type FormResult, type FormSchemaResult, type FormatProfile, 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 MeasureOptions, 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 PlaceSealResult, type PrintOptions, type PrintPreset, type RenderSvgOptions, type RenderSvgResult, SPACE_EM_FIXED, SPACE_EM_FONT, type ScanCell, type ScanParaKind, type ScanParagraph, type ScanTable, type SealOp, type SealPlacement, type SectionScan, type SpliceEdit, type TRange, type TableProfile, VERSION, type ValidateIssue, type ValidateResult, ValueCursor, type WarningCode, type WatchOptions, type WrapMode, type WrapResult, applySplices, blocksToMarkdown, blocksToPdf, buildParagraphSplices, buildRangeSplices, charWidthEm1000, compare, detectFormat, detectOle2Format, detectZipFormat, diffBlocks, extractFormFields, extractFormSchema, fillForm, fillFormFields, fillHwpx, fillWithUniqueGuard, fitRatioForFewerLines, formatFillValue, hwpxToProfile, inferFieldType, isHwpxFile, isKnownFont, isLabelCell, isOldHwpFile, isPdfFile, isZipFile, markdownToHwpx, markdownToPdf, measureTextWidth, normalizeGongmunPreset, openHwpxDocument, parse, parseDocx, parseHwp, parseHwp3, parseHwpml, parseHwpx, parsePdf, parseXls, parseXlsx, patchHwp, patchHwpx, patchHwpxBlocks, placeSealHwpx, renderHtml, renderHwpxToSvg, scanSectionXml, simulateWrap, simulateWrapKeepWord, unknownFontWarnings, validateHwpx };
1453
+ export { type BlockCapability, type BlockCapabilityInfo, type BlockDiff, type BlockEdit, type BlockSourceRef, type BorderDef, type BorderFillDef, type BoundingBox, type CellCapability, type CellContext, type CellDiff, type CellProfile, type CharPrDef, type DiffChangeType, type DiffResult, type DocumentMetadata, type DocumentQualitySummary, type ErrorCode, type ExtractedImage, type FileType, type FillFormOutput, type FillInput, type FillOutputFormat, type FillResult, type FillValue, type FormField, type FormFieldSchema, type FormFieldType, type FormResult, type FormSchemaResult, type FormatProfile, type GongmunFont, type GongmunLintFinding, 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 MeasureOptions, 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 PlaceSealResult, type PrintOptions, type PrintPreset, type RenderSvgOptions, type RenderSvgResult, SPACE_EM_FIXED, SPACE_EM_FONT, type ScanCell, type ScanParaKind, type ScanParagraph, type ScanTable, type SealOp, type SealPlacement, type SectionScan, type SpliceEdit, type TRange, type TableProfile, VERSION, type ValidateIssue, type ValidateResult, ValueCursor, type WarningCode, type WatchOptions, type WrapMode, type WrapResult, applySplices, blocksToMarkdown, blocksToPdf, buildParagraphSplices, buildRangeSplices, charWidthEm1000, compare, detectFormat, detectOle2Format, detectZipFormat, diffBlocks, extractFormFields, extractFormSchema, fillForm, fillFormFields, fillHwpx, fillWithUniqueGuard, fitRatioForFewerLines, formatFillValue, gongmunLintWarnings, hwpxToProfile, inferFieldType, isHwpxFile, isKnownFont, isLabelCell, isOldHwpFile, isPdfFile, isZipFile, lintGongmunText, markdownToHwpx, markdownToPdf, measureTextWidth, normalizeGongmunPreset, openHwpxDocument, parse, parseDocx, parseHwp, parseHwp3, parseHwpml, parseHwpx, parsePdf, parseXls, parseXlsx, patchHwp, patchHwpx, patchHwpxBlocks, placeSealHwpx, renderHtml, renderHwpxToSvg, scanSectionXml, simulateWrap, simulateWrapKeepWord, unknownFontWarnings, validateHwpx };
package/dist/index.d.ts CHANGED
@@ -619,7 +619,7 @@ interface GongmunOptions {
619
619
  bodyPt?: number;
620
620
  /** 본문 줄간격(%). 기본 160 (회의록 130) */
621
621
  lineSpacing?: number;
622
- /** 항목부호 체계. 'standard'=법정 8단계(1. 가. 1) …) / 'report'=보고서 불릿(□ ○ - ㆍ) / 'gaejosik'=개조식(□ ○ ㆍ + 부호별 폰트) */
622
+ /** 항목부호 체계. 'standard'=법정 8단계(1. 가. 1) …) / 'report'=보고서 불릿(□ ○ - ㆍ) / 'gaejosik'=개조식(□ ○ - ㆍ + 부호별 폰트) */
623
623
  numbering?: GongmunNumbering;
624
624
  /**
625
625
  * 표지 페이지(개조식 프리셋 기본 켜짐) — 첫 h1을 제목으로, 파랑 장식 바 + 날짜 + 기관명.
@@ -650,8 +650,9 @@ interface GongmunOptions {
650
650
  };
651
651
  /**
652
652
  * 요소별 글꼴 오버라이드 — 기관 표준 폰트 적용이나 미설치 폰트 대체용.
653
- * body=본문(개조식 ○·―) / heading=제목 계열(□·장헤더·표지·목차) / ref=※ 참고 / table=표 셀.
654
- * 개조식 외 프리셋은 body만 적용된다 (bodyFont보다 우선).
653
+ * body=본문(개조식 ○·-) / heading=제목 계열(□·장헤더·표지·목차) / ref=※ 참고 / table=표 셀.
654
+ * 실측 폰트 프리셋(개조식·보고서·계획서)은 네 역할 전부, 그 외 프리셋은 body만
655
+ * 적용된다 (bodyFont보다 우선).
655
656
  */
656
657
  fonts?: {
657
658
  body?: string;
@@ -669,6 +670,12 @@ interface GongmunOptions {
669
670
  approval?: string[];
670
671
  /** 본문 첫 페이지 제목 박스(개조식) — 목차 뒤 본문 시작에 제목 반복(실측 관행). 기본: 표지 있으면 켜짐 */
671
672
  bodyTitleBox?: boolean;
673
+ /**
674
+ * h2 섹션 제목 말머리 (비개조식 — 개조식 h2는 로마숫자 장 헤더가 대체).
675
+ * 'box'=□ 대항목(실측 보고서 양식 관행) / 'number'=아라비아 번호(1. 2. — 공고문 관행)
676
+ * / 'none'=말머리 없음. 기본: 보고서·계획서 'box', 그 외 'none'.
677
+ */
678
+ h2Marker?: "box" | "number" | "none";
672
679
  }
673
680
  /** 프리셋 별칭(한글/영문) → 내부 preset 키. CLI·라이브러리 공용 */
674
681
  declare const PRESET_ALIAS: Record<string, GongmunPreset>;
@@ -852,6 +859,33 @@ declare function isKnownFont(name: string): boolean;
852
859
  */
853
860
  declare function unknownFontWarnings(fonts: Record<string, string | undefined>): string[];
854
861
 
862
+ /**
863
+ * 공문서 표기법 검수기 — 「행정업무의 운영 및 혁신에 관한 규정」 시행규칙 및
864
+ * 행정안전부 행정업무운영 편람의 날짜·시간·금액·기호 표기법을 정규식으로 검사.
865
+ *
866
+ * 원전: jkf87/hwpx-skill gonmun_lint.py(2025 편람 기준 13룰)를 kordoc에 맞게 이식
867
+ * (v4.0.1). URL 쌍점 오탐 가드 등 일부 보강. 검사는 조언용이다 — 생성은 막지 않고
868
+ * 경고만 낸다 (A2 폰트경고와 같은 원칙). 별도 CLI `kordoc lint`는 error 시 exit 1.
869
+ */
870
+ interface GongmunLintFinding {
871
+ /** 1-based 줄 번호 */
872
+ line: number;
873
+ /** 걸린 원문 조각 */
874
+ match: string;
875
+ /** 규칙 코드 (DATE_NO_SPACE 등) */
876
+ rule: string;
877
+ severity: "error" | "warning";
878
+ message: string;
879
+ suggest?: string;
880
+ }
881
+ /**
882
+ * 텍스트(마크다운 포함) 표기법 검수. 마크다운 펜스 코드블록(``` ~ ```) 안은
883
+ * 건너뛴다 — 코드·URL이 날짜/쌍점 규칙에 오탐되는 것 방지.
884
+ */
885
+ declare function lintGongmunText(text: string): GongmunLintFinding[];
886
+ /** 검수 결과를 사람이 읽는 경고 문자열 배열로 — generate 경고 채널(A2와 동일)용 */
887
+ declare function gongmunLintWarnings(text: string, limit?: number): string[];
888
+
855
889
  /**
856
890
  * 한글 조판 텍스트 폭 계산 — 함초롬바탕(HCR Batang) 실측 advance 테이블
857
891
  *
@@ -1416,4 +1450,4 @@ interface FillFormOutput {
1416
1450
  */
1417
1451
  declare function fillForm(input: string | ArrayBuffer | Buffer, values: Record<string, FillInput>, outputFormat?: FillOutputFormat): Promise<FillFormOutput>;
1418
1452
 
1419
- export { type BlockCapability, type BlockCapabilityInfo, type BlockDiff, type BlockEdit, type BlockSourceRef, type BorderDef, type BorderFillDef, type BoundingBox, type CellCapability, type CellContext, type CellDiff, type CellProfile, type CharPrDef, type DiffChangeType, type DiffResult, type DocumentMetadata, type DocumentQualitySummary, type ErrorCode, type ExtractedImage, type FileType, type FillFormOutput, type FillInput, type FillOutputFormat, type FillResult, type FillValue, type FormField, type FormFieldSchema, type FormFieldType, type FormResult, type FormSchemaResult, type FormatProfile, 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 MeasureOptions, 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 PlaceSealResult, type PrintOptions, type PrintPreset, type RenderSvgOptions, type RenderSvgResult, SPACE_EM_FIXED, SPACE_EM_FONT, type ScanCell, type ScanParaKind, type ScanParagraph, type ScanTable, type SealOp, type SealPlacement, type SectionScan, type SpliceEdit, type TRange, type TableProfile, VERSION, type ValidateIssue, type ValidateResult, ValueCursor, type WarningCode, type WatchOptions, type WrapMode, type WrapResult, applySplices, blocksToMarkdown, blocksToPdf, buildParagraphSplices, buildRangeSplices, charWidthEm1000, compare, detectFormat, detectOle2Format, detectZipFormat, diffBlocks, extractFormFields, extractFormSchema, fillForm, fillFormFields, fillHwpx, fillWithUniqueGuard, fitRatioForFewerLines, formatFillValue, hwpxToProfile, inferFieldType, isHwpxFile, isKnownFont, isLabelCell, isOldHwpFile, isPdfFile, isZipFile, markdownToHwpx, markdownToPdf, measureTextWidth, normalizeGongmunPreset, openHwpxDocument, parse, parseDocx, parseHwp, parseHwp3, parseHwpml, parseHwpx, parsePdf, parseXls, parseXlsx, patchHwp, patchHwpx, patchHwpxBlocks, placeSealHwpx, renderHtml, renderHwpxToSvg, scanSectionXml, simulateWrap, simulateWrapKeepWord, unknownFontWarnings, validateHwpx };
1453
+ export { type BlockCapability, type BlockCapabilityInfo, type BlockDiff, type BlockEdit, type BlockSourceRef, type BorderDef, type BorderFillDef, type BoundingBox, type CellCapability, type CellContext, type CellDiff, type CellProfile, type CharPrDef, type DiffChangeType, type DiffResult, type DocumentMetadata, type DocumentQualitySummary, type ErrorCode, type ExtractedImage, type FileType, type FillFormOutput, type FillInput, type FillOutputFormat, type FillResult, type FillValue, type FormField, type FormFieldSchema, type FormFieldType, type FormResult, type FormSchemaResult, type FormatProfile, type GongmunFont, type GongmunLintFinding, 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 MeasureOptions, 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 PlaceSealResult, type PrintOptions, type PrintPreset, type RenderSvgOptions, type RenderSvgResult, SPACE_EM_FIXED, SPACE_EM_FONT, type ScanCell, type ScanParaKind, type ScanParagraph, type ScanTable, type SealOp, type SealPlacement, type SectionScan, type SpliceEdit, type TRange, type TableProfile, VERSION, type ValidateIssue, type ValidateResult, ValueCursor, type WarningCode, type WatchOptions, type WrapMode, type WrapResult, applySplices, blocksToMarkdown, blocksToPdf, buildParagraphSplices, buildRangeSplices, charWidthEm1000, compare, detectFormat, detectOle2Format, detectZipFormat, diffBlocks, extractFormFields, extractFormSchema, fillForm, fillFormFields, fillHwpx, fillWithUniqueGuard, fitRatioForFewerLines, formatFillValue, gongmunLintWarnings, hwpxToProfile, inferFieldType, isHwpxFile, isKnownFont, isLabelCell, isOldHwpFile, isPdfFile, isZipFile, lintGongmunText, markdownToHwpx, markdownToPdf, measureTextWidth, normalizeGongmunPreset, openHwpxDocument, parse, parseDocx, parseHwp, parseHwp3, parseHwpml, parseHwpx, parsePdf, parseXls, parseXlsx, patchHwp, patchHwpx, patchHwpxBlocks, placeSealHwpx, renderHtml, renderHwpxToSvg, scanSectionXml, simulateWrap, simulateWrapKeepWord, unknownFontWarnings, validateHwpx };
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  sanitizeHref,
21
21
  stripDtd,
22
22
  toArrayBuffer
23
- } from "./chunk-IA5UZZKV.js";
23
+ } from "./chunk-3H3CVOZ3.js";
24
24
  import {
25
25
  parsePageRange
26
26
  } from "./chunk-GE43BE46.js";
@@ -20950,7 +20950,7 @@ function fitRatioForFewerLines(text, firstWidth, contWidth, height, baseRatio, m
20950
20950
  }
20951
20951
 
20952
20952
  // src/hwpx/gaejosik.ts
20953
- var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "\u2015", "\u318D"];
20953
+ var GAEJOSIK_BULLETS = ["\u25A1", "\u25CB", "-", "\u318D"];
20954
20954
  function gaejosikMarker(depth) {
20955
20955
  return GAEJOSIK_BULLETS[Math.min(depth, GAEJOSIK_BULLETS.length - 1)];
20956
20956
  }
@@ -21097,6 +21097,9 @@ function normalizeGongmunPreset(preset) {
21097
21097
  if (!preset) return "official";
21098
21098
  return PRESET_ALIAS[preset.trim()] ?? "official";
21099
21099
  }
21100
+ function usesReportFonts(preset) {
21101
+ return preset === "gaejosik" || preset === "report" || preset === "plan";
21102
+ }
21100
21103
  function resolveGongmun(opts) {
21101
21104
  const preset = normalizeGongmunPreset(opts.preset);
21102
21105
  const d = PRESET_DEFAULTS[preset];
@@ -21125,7 +21128,9 @@ function resolveGongmun(opts) {
21125
21128
  endMark: opts.endMark ?? preset === "official",
21126
21129
  approval: opts.approval && opts.approval.length > 0 ? opts.approval : null,
21127
21130
  // 본문 제목박스 — 실측(GT3·GT12): 목차 뒤 본문 시작에 제목 반복. 표지 켜진 개조식 기본
21128
- bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn)
21131
+ bodyTitleBox: opts.bodyTitleBox ?? (gaejosik && coverOn),
21132
+ // h2 말머리 — 실측 보고서 양식(부처별 3종)의 섹션 제목이 곧 □ 대항목 (QA-2)
21133
+ h2Marker: opts.h2Marker ?? (preset === "report" || preset === "plan" ? "box" : "none")
21129
21134
  };
21130
21135
  }
21131
21136
  var HANGUL_INITIALS = [0, 2, 3, 5, 6, 7, 9, 11, 12, 14, 15, 16, 17, 18];
@@ -21270,10 +21275,10 @@ function headingCharPrId(level) {
21270
21275
  if (level === 3) return CHAR_H3;
21271
21276
  return CHAR_H4;
21272
21277
  }
21273
- function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_COLOR, ratioPct = 100, keepFontOnBold = false) {
21278
+ function charPr(id, height, bold, italic, fontId = 0, textColor = DEFAULT_TEXT_COLOR, ratioPct = 100) {
21274
21279
  const boldAttr = bold ? ` bold="1"` : "";
21275
21280
  const italicAttr = italic ? ` italic="1"` : "";
21276
- const effFont = bold && !keepFontOnBold ? 2 : fontId;
21281
+ const effFont = fontId;
21277
21282
  const boldEl = bold ? `<hh:bold/>` : "";
21278
21283
  return ` <hh:charPr id="${id}" height="${height}" textColor="${textColor}" shadeColor="none" useFontSpace="0" useKerning="0" symMark="NONE" borderFillIDRef="1"${boldAttr}${italicAttr}>
21279
21284
  <hh:fontRef hangul="${effFont}" latin="${effFont}" hanja="${effFont}" japanese="${effFont}" other="${effFont}" symbol="${effFont}" user="${effFont}"/>
@@ -21364,8 +21369,8 @@ function pageHidingCtrl(hideHeader = false) {
21364
21369
  return `<hp:ctrl><hp:pageHiding hideHeader="${hideHeader ? 1 : 0}" hideFooter="0" hideMasterPage="0" hideBorder="0" hideFill="0" hidePageNum="1"/></hp:ctrl>`;
21365
21370
  }
21366
21371
  var CHAR_VARIANT_BASE = 11;
21367
- function charVariantBase(gaejosik) {
21368
- return gaejosik ? CHAR_VARIANT_BASE + GJ_CHAR_COUNT : CHAR_VARIANT_BASE;
21372
+ function charVariantBase(measured) {
21373
+ return measured ? CHAR_VARIANT_BASE + GJ_CHAR_COUNT : CHAR_VARIANT_BASE;
21369
21374
  }
21370
21375
  var GONGMUN_BODY_RATIO = 95;
21371
21376
 
@@ -21613,7 +21618,7 @@ function generateManifest(chartParts = []) {
21613
21618
  </opf:package>`;
21614
21619
  }
21615
21620
  function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls = []) {
21616
- const gaejosik = gongmun?.preset === "gaejosik";
21621
+ const measured = !!gongmun && usesReportFonts(gongmun.preset);
21617
21622
  let body = 1e3, code = 900, h1 = 1800, h2 = 1400, h3 = 1200, h4 = 1100;
21618
21623
  if (gongmun) {
21619
21624
  body = gongmun.bodyHeight;
@@ -21623,7 +21628,9 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
21623
21628
  h3 = body;
21624
21629
  h4 = Math.max(body - 100, 1300);
21625
21630
  }
21626
- const bodyFont = gaejosik ? 4 : 0;
21631
+ const bodyFont = measured ? 4 : 0;
21632
+ const hFont = measured ? 3 : 1;
21633
+ const hBold = !measured;
21627
21634
  const bodyRatio = gongmun ? GONGMUN_BODY_RATIO : 100;
21628
21635
  const rows = [
21629
21636
  charPr(0, body, false, false, bodyFont, theme.body, bodyRatio),
@@ -21631,25 +21638,25 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
21631
21638
  charPr(2, body, false, true, bodyFont, theme.body, bodyRatio),
21632
21639
  charPr(3, body, true, true, bodyFont, theme.body, bodyRatio),
21633
21640
  charPr(4, code, false, false, 1),
21634
- charPr(5, h1, true, false, 1, theme.h1),
21635
- charPr(6, h2, true, false, 1, theme.h2),
21636
- charPr(7, h3, true, false, 1, theme.h3),
21637
- charPr(8, h4, true, false, 1, theme.h4),
21641
+ charPr(5, h1, hBold, false, hFont, theme.h1),
21642
+ charPr(6, h2, hBold, false, hFont, theme.h2),
21643
+ charPr(7, h3, hBold, false, hFont, theme.h3),
21644
+ charPr(8, h4, hBold, false, hFont, theme.h4),
21638
21645
  charPr(CHAR_TABLE_HEADER, body, theme.tableHeaderBold, false, bodyFont, theme.tableHeader),
21639
21646
  charPr(CHAR_QUOTE, body, false, true, bodyFont, theme.quote)
21640
21647
  ];
21641
- if (gaejosik) {
21648
+ if (measured) {
21642
21649
  const sz = gaejosikSizes(body, gongmun.sizes);
21643
21650
  rows.push(
21644
21651
  charPr(11, sz.dae, false, false, 3),
21645
21652
  // □ HY헤드라인M
21646
- charPr(12, sz.dae, true, false, 3, "#000000", 100, true),
21653
+ charPr(12, sz.dae, true, false, 3),
21647
21654
  // □ bold
21648
21655
  charPr(13, sz.cham, false, false, 5),
21649
21656
  // ※ 한양중고딕
21650
- charPr(14, sz.cham, true, false, 5, "#000000", 100, true),
21657
+ charPr(14, sz.cham, true, false, 5),
21651
21658
  // ※ bold
21652
- charPr(15, sz.chapter, true, false, 4, "#FFFFFF", 100, true),
21659
+ charPr(15, sz.chapter, true, false, 4, "#FFFFFF"),
21653
21660
  // 장 로마숫자(흰)
21654
21661
  charPr(16, sz.chapter, false, false, 3),
21655
21662
  // 장 제목
@@ -21657,15 +21664,15 @@ function buildCharProperties(theme, gongmun, ratioVariants = [], extraCharPrXmls
21657
21664
  // 표지 제목
21658
21665
  charPr(18, sz.coverSub, false, false, 3),
21659
21666
  // 표지 날짜·기관명
21660
- charPr(19, sz.tocLabel, true, false, 3, "#000000", 100, true),
21667
+ charPr(19, sz.tocLabel, true, false, 3),
21661
21668
  // 목 차
21662
- charPr(20, sz.tocRoman, true, false, 6, "#000000", 100, true),
21669
+ charPr(20, sz.tocRoman, true, false, 6),
21663
21670
  // 목차 로마숫자(한양신명조)
21664
21671
  charPr(21, sz.tocItem, false, false, 3),
21665
21672
  // 목차 항목
21666
21673
  charPr(22, sz.table, false, false, 7),
21667
21674
  // 표 셀(맑은 고딕 12pt)
21668
- charPr(23, sz.table, true, false, 7, "#000000", 100, true),
21675
+ charPr(23, sz.table, true, false, 7),
21669
21676
  // 표 셀 bold
21670
21677
  charPr(24, sz.bar, false, false, 4),
21671
21678
  // 표지 바 셀 빈 문단(6pt)
@@ -21704,10 +21711,11 @@ ${base2.join("\n")}
21704
21711
  }
21705
21712
  const ls = gongmun.lineSpacing;
21706
21713
  const titleAlign = gongmun.centerTitle ? "CENTER" : "LEFT";
21714
+ const h2Geom = gongmun.h2Marker !== "none" ? { spaceBefore: Math.round(gongmun.bodyHeight * 2), spaceAfter: 0, indent: -markerWidth(gongmun.h2Marker === "box" ? "\u25A1" : "1.", 1600) } : { spaceBefore: 600, spaceAfter: 150 };
21707
21715
  const base = [
21708
21716
  paraPr(0, { lineSpacing: ls, keepWord: true }),
21709
21717
  paraPr(1, { align: titleAlign, spaceBefore: 400, spaceAfter: 400, lineSpacing: ls, keepWord: true }),
21710
- paraPr(2, { align: "LEFT", spaceBefore: 600, spaceAfter: 150, lineSpacing: ls, keepWord: true }),
21718
+ paraPr(2, { align: "LEFT", ...h2Geom, lineSpacing: ls, keepWord: true }),
21711
21719
  paraPr(3, { align: "LEFT", spaceBefore: 400, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
21712
21720
  paraPr(4, { align: "LEFT", spaceBefore: 300, spaceAfter: 100, lineSpacing: ls, keepWord: true }),
21713
21721
  paraPr(5, { align: "LEFT", lineSpacing: 130, indent: 400, keepWord: true }),
@@ -21736,6 +21744,11 @@ ${base2.join("\n")}
21736
21744
  // 표지 장식 바 셀 빈 문단 — 저줄간격(실측 71%)으로 바 높이 818 안에 수납
21737
21745
  paraPr(GJ_PARA_BAR, { align: "CENTER", lineSpacing: 70, keepWord: true })
21738
21746
  );
21747
+ } else if (usesReportFonts(gongmun.preset)) {
21748
+ const cham = gaejosikChamIndent(gongmun.bodyHeight, gongmun.sizes);
21749
+ base.push(
21750
+ paraPr(GJ_PARA_CHAM, { align: "JUSTIFY", lineSpacing: ls, left: cham.left, indent: cham.indent, spaceBefore: gaejosikSpaceBefore(3, gongmun.bodyHeight), keepWord: true })
21751
+ );
21739
21752
  }
21740
21753
  return `<hh:paraProperties itemCnt="${base.length}">
21741
21754
  ${base.join("\n")}
@@ -21758,14 +21771,15 @@ function fontEntry(id, face, weight = 6) {
21758
21771
  </hh:font>`;
21759
21772
  }
21760
21773
  function buildFontFaces(gongmun, bodyFace) {
21761
- if (gongmun?.preset === "gaejosik") {
21774
+ if (gongmun && usesReportFonts(gongmun.preset)) {
21762
21775
  const ov = gongmun.fonts;
21776
+ const bodyDefault = gongmun.preset !== "gaejosik" && gongmun.bodyFont === "gothic" ? "\uB9D1\uC740 \uACE0\uB515" : "\uD734\uBA3C\uBA85\uC870";
21763
21777
  const faces = [
21764
21778
  "\uD568\uCD08\uB86C\uBC14\uD0D5",
21765
21779
  "\uD568\uCD08\uB86C\uB3CB\uC6C0",
21766
21780
  "HY\uACAC\uACE0\uB515",
21767
21781
  ov.heading ?? "HY\uD5E4\uB4DC\uB77C\uC778M",
21768
- ov.body ?? "\uD734\uBA3C\uBA85\uC870",
21782
+ ov.body ?? bodyDefault,
21769
21783
  ov.ref ?? "\uD55C\uC591\uC911\uACE0\uB515",
21770
21784
  "\uD55C\uC591\uC2E0\uBA85\uC870",
21771
21785
  ov.table ?? "\uB9D1\uC740 \uACE0\uB515"
@@ -23440,12 +23454,13 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23440
23454
  const orderedCounters = {};
23441
23455
  let prevWasOrdered = false;
23442
23456
  const gaejosik = gongmun?.preset === "gaejosik";
23443
- const vBase = charVariantBase(gaejosik);
23457
+ const measured = !!gongmun && usesReportFonts(gongmun.preset);
23458
+ const vBase = charVariantBase(measured);
23444
23459
  const tableStyle = gongmun ? {
23445
23460
  totalWidth: mmToHwpunit(210 - gongmun.margins.left - gongmun.margins.right),
23446
- charPr: gaejosik ? GJ_CHAR_TABLE : CHAR_NORMAL,
23447
- boldCharPr: gaejosik ? GJ_CHAR_TABLE_BOLD : CHAR_BOLD,
23448
- charHeight: gaejosik ? gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).table : gongmun.bodyHeight,
23461
+ charPr: measured ? GJ_CHAR_TABLE : CHAR_NORMAL,
23462
+ boldCharPr: measured ? GJ_CHAR_TABLE_BOLD : CHAR_BOLD,
23463
+ charHeight: measured ? gaejosikSizes(gongmun.bodyHeight, gongmun.sizes).table : gongmun.bodyHeight,
23449
23464
  headerBf: gongmunTableHeaderBf(gaejosik),
23450
23465
  centerParaPr: GONGMUN_CENTER,
23451
23466
  tblCenterParaPr: GONGMUN_TBL_CENTER,
@@ -23455,6 +23470,7 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23455
23470
  } : null;
23456
23471
  const chamMap = (id) => id === CHAR_BOLD ? GJ_CHAR_CHAM_BOLD : id;
23457
23472
  let chapterNo = 0;
23473
+ let h2Seq = 0;
23458
23474
  let coverH1Idx = -1;
23459
23475
  let pendingPageBreak = false;
23460
23476
  let pendingNewNum = false;
@@ -23508,7 +23524,7 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23508
23524
  switch (block.type) {
23509
23525
  case "heading": {
23510
23526
  if (gongmun && blockIdx === titleBoxH1Idx && tableStyle && bfReg) {
23511
- xml = buildTitleBox((block.text || "").trim(), CHAR_H1, tableStyle.totalWidth, bfReg);
23527
+ xml = buildTitleBox((block.text || "").trim(), measured ? GJ_CHAR_BODY_TITLE : CHAR_H1, tableStyle.totalWidth, bfReg);
23512
23528
  break;
23513
23529
  }
23514
23530
  if (gaejosik) {
@@ -23532,11 +23548,16 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23532
23548
  const pId = headingParaPrId(block.level || 1);
23533
23549
  const cId = headingCharPrId(block.level || 1);
23534
23550
  const styleId = gongmun ? Math.min(block.level || 1, 4) : 0;
23535
- xml = generateParagraph(block.text || "", pId, cId, void 0, styleId);
23551
+ let hText = block.text || "";
23552
+ if (gongmun && (block.level || 1) === 2 && gongmun.h2Marker !== "none") {
23553
+ const title = stripChapterNumber(hText);
23554
+ hText = gongmun.h2Marker === "box" ? `\u25A1 ${title}` : `${++h2Seq}. ${title}`;
23555
+ }
23556
+ xml = generateParagraph(hText, pId, cId, void 0, styleId);
23536
23557
  break;
23537
23558
  }
23538
23559
  case "paragraph": {
23539
- if (gaejosik && (block.text || "").trimStart().startsWith("\u203B")) {
23560
+ if (measured && (block.text || "").trimStart().startsWith("\u203B")) {
23540
23561
  xml = generateParagraph((block.text || "").trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
23541
23562
  break;
23542
23563
  }
@@ -23581,7 +23602,7 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23581
23602
  break;
23582
23603
  }
23583
23604
  case "blockquote": {
23584
- if (gaejosik) {
23605
+ if (measured) {
23585
23606
  const t = (block.text || "").trim();
23586
23607
  if (t) xml = generateParagraph(t.startsWith("\u203B") ? t : `\u203B ${t}`, GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
23587
23608
  break;
@@ -23599,14 +23620,14 @@ function blocksToSectionXml(blocks, theme, gongmun, gongmunList = gongmun ? prec
23599
23620
  const depth = info?.depth ?? 0;
23600
23621
  const marker2 = info?.marker ?? "";
23601
23622
  const content = block.text || "";
23602
- if (gaejosik && content.trimStart().startsWith("\u203B")) {
23623
+ if (measured && content.trimStart().startsWith("\u203B")) {
23603
23624
  xml = generateParagraph(content.trim(), GJ_PARA_CHAM, GJ_CHAR_CHAM, chamMap);
23604
23625
  break;
23605
23626
  }
23606
23627
  const text = marker2 ? `${marker2} ${content}` : content;
23607
23628
  let listCharPr = gongmun.numbering === "report" && depth === 0 ? CHAR_BOLD : CHAR_NORMAL;
23608
23629
  let mapId = fit ? variantMapper(fit, blockIdx, vBase) : void 0;
23609
- if (gaejosik && depth === 0) {
23630
+ if (measured && depth === 0 && gongmun.numbering !== "standard") {
23610
23631
  listCharPr = GJ_CHAR_DAE;
23611
23632
  mapId = (id) => id === CHAR_BOLD ? GJ_CHAR_DAE_BOLD : id;
23612
23633
  }
@@ -23702,11 +23723,12 @@ async function markdownToHwpx(markdown, options) {
23702
23723
  const theme = resolveTheme(options?.theme);
23703
23724
  const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
23704
23725
  const gaejosik = gongmun?.preset === "gaejosik";
23726
+ const measured = !!gongmun && usesReportFonts(gongmun.preset);
23705
23727
  const blocks = parseMarkdownToBlocks(markdown);
23706
23728
  const gongmunList = gongmun ? precomputeGongmunList(blocks, gongmun) : null;
23707
23729
  const fit = gongmun && gongmunList ? computeGongmunFitPlan(blocks, gongmun, gongmunList) : null;
23708
23730
  const staticBfEnd = gongmun ? gaejosik ? 11 : 4 : 3;
23709
- const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(gaejosik) + (fit?.variants?.length ?? 0) * 4, staticBfEnd) : null;
23731
+ const remap = options?.profile ? buildProfileRemap(options.profile, charVariantBase(measured) + (fit?.variants?.length ?? 0) * 4, staticBfEnd) : null;
23710
23732
  const bfReg = gongmun ? new TableBfRegistry(staticBfEnd + (remap?.borderFillXmls.length ?? 0)) : null;
23711
23733
  const chartParts = [];
23712
23734
  const sectionXml = blocksToSectionXml(blocks, theme, gongmun, gongmunList, fit, chartParts, bfReg, remap);
@@ -24704,6 +24726,142 @@ function unknownFontWarnings(fonts) {
24704
24726
  return warnings;
24705
24727
  }
24706
24728
 
24729
+ // src/hwpx/gongmun-lint.ts
24730
+ var RULES = [
24731
+ // 날짜 ─ 온점 뒤 한 칸, 0 패딩 금지, 연도 4자리, 끝 마침표
24732
+ {
24733
+ code: "DATE_NO_SPACE",
24734
+ severity: "error",
24735
+ pattern: /\b\d{4}\.\d{1,2}\.\d{1,2}\.?/g,
24736
+ message: "\uB0A0\uC9DC \uC628\uC810 \uB4A4\uC5D0 \uD55C \uCE78\uC529 \uB744\uC6CC\uC57C \uD568",
24737
+ suggest: "\uC608) 2025. 1. 6."
24738
+ },
24739
+ {
24740
+ code: "DATE_ZERO_PAD",
24741
+ severity: "error",
24742
+ pattern: /\b\d{4}\.\s*0\d\.|\.\s*0\d\./g,
24743
+ message: "\uC6D4\xB7\uC77C \uC55E\uC758 '0'\uC740 \uD45C\uAE30\uD558\uC9C0 \uC54A\uC74C",
24744
+ suggest: "\uC608) 2025. 1. 6. (2025. 01. 06. \u2715)"
24745
+ },
24746
+ {
24747
+ code: "DATE_2DIGIT_YR",
24748
+ severity: "error",
24749
+ pattern: /(?<!\d)['’]\d{2}\.\s*\d/g,
24750
+ message: "\uC5F0\uB3C4\uB294 \uB124 \uC790\uB9AC\uB85C \uD45C\uAE30('24 \u2715)",
24751
+ suggest: "\uC608) 2025. 1. 6."
24752
+ },
24753
+ {
24754
+ code: "DATE_NO_END_DOT",
24755
+ severity: "warning",
24756
+ pattern: /\b\d{4}\.\s\d{1,2}\.\s\d{1,2}(?!\s*[.\d(])/g,
24757
+ message: "\uB0A0\uC9DC\uC758 '\uC77C' \uB2E4\uC74C\uC5D0 \uB9C8\uCE68\uD45C(.)\uB97C \uCC0D\uC5B4\uC57C \uD568",
24758
+ suggest: "\uC608) 2025. 1. 6."
24759
+ },
24760
+ // 시간 ─ 24시각제, 쌍점 붙여쓰기
24761
+ {
24762
+ code: "TIME_AMPM",
24763
+ severity: "error",
24764
+ pattern: /(오전|오후|아침|밤|낮)\s*\d{1,2}\s*시/g,
24765
+ message: "24\uC2DC\uAC01\uC81C \uC22B\uC790\uB85C \uD45C\uAE30(\uC624\uC804/\uC624\uD6C4 \uC0AC\uC6A9 \uC548 \uD568)",
24766
+ suggest: "\uC608) 09:00, 15:30"
24767
+ },
24768
+ {
24769
+ code: "TIME_24H",
24770
+ severity: "warning",
24771
+ pattern: /(?<!\d)24\s*시(?!각)/g,
24772
+ message: "'24\uC2DC'\uBCF4\uB2E4 \uC775\uC77C 00:00 \uB610\uB294 '18:00\uAE4C\uC9C0' \uAD8C\uC7A5",
24773
+ suggest: "\uC608) 18:00"
24774
+ },
24775
+ {
24776
+ code: "TIME_COLON_SP",
24777
+ severity: "error",
24778
+ pattern: /\b\d{1,2}\s+:\s*\d{2}\b|\b\d{1,2}:\s+\d{2}\b/g,
24779
+ message: "\uC2DC\uC640 \uBD84 \uC0AC\uC774 \uC30D\uC810\uC740 \uC591\uCABD\uC744 \uBD99\uC5EC \uC500",
24780
+ suggest: "\uC608) 13:20"
24781
+ },
24782
+ // 금액 ─ '천원' 금지, 금+숫자 붙여쓰기
24783
+ {
24784
+ code: "MONEY_CHEONWON",
24785
+ severity: "error",
24786
+ pattern: /\d+\s*천\s*원/g,
24787
+ message: "\uAE08\uC561\uC740 '\uCC9C\uC6D0'\uC73C\uB85C \uC904\uC774\uC9C0 \uC54A\uACE0 \uC544\uB77C\uBE44\uC544 \uC22B\uC790\uB85C",
24788
+ suggest: "\uC608) 345,000\uC6D0"
24789
+ },
24790
+ {
24791
+ code: "MONEY_GEUM_SP",
24792
+ severity: "warning",
24793
+ pattern: /금\s+\d/g,
24794
+ message: "'\uAE08'\uACFC \uC22B\uC790 \uC0AC\uC774\uB294 \uBD99\uC5EC \uC4F0\uB294 \uAC83\uC774 \uC6D0\uCE59",
24795
+ suggest: "\uC608) \uAE08113,560\uC6D0"
24796
+ },
24797
+ // 붙임 ─ 쌍점 금지(2타 띄움)
24798
+ {
24799
+ code: "BUNIM_COLON",
24800
+ severity: "error",
24801
+ pattern: /붙\s*임\s*:/g,
24802
+ message: "'\uBD99\uC784' \uB2E4\uC74C\uC5D0 \uC30D\uC810(:)\uC744 \uBD99\uC774\uC9C0 \uC54A\uC74C(2\uD0C0 \uB744\uC6C0)",
24803
+ suggest: "\uC608) \uBD99\uC784 \uACC4\uD68D\uC11C 1\uBD80."
24804
+ },
24805
+ // 표기 ─ 물결표+까지 중복, 한글 먼저, 쌍점 띄어쓰기
24806
+ {
24807
+ code: "KKAJI_DUP",
24808
+ severity: "error",
24809
+ pattern: /[∼~~][^\n]{0,20}?까지/g,
24810
+ message: "\uBB3C\uACB0\uD45C(\u223C)\uC640 '\uAE4C\uC9C0'\uB97C \uD568\uAED8 \uC4F0\uC9C0 \uC54A\uC74C",
24811
+ suggest: "\uC608) 2. 20.\u223C2. 24."
24812
+ },
24813
+ {
24814
+ code: "FOREIGN_FIRST",
24815
+ severity: "warning",
24816
+ pattern: /\b[A-Z]{2,5}\s*\([가-힣]/g,
24817
+ message: "\uD55C\uAE00\uC744 \uBA3C\uC800 \uC4F0\uACE0 \uAD04\uD638 \uC548\uC5D0 \uC678\uAD6D\uC5B4\uB97C \uBCD1\uAE30",
24818
+ suggest: "\uC608) \uC5C5\uBB34 \uD611\uC57D(MOU)"
24819
+ },
24820
+ // 쌍점 — URL(https:// 등)·시각(13:20)은 제외
24821
+ {
24822
+ code: "COLON_SPACE",
24823
+ severity: "warning",
24824
+ pattern: /\S\s+:\S|\S:(?!\/\/)[^\s\d]/g,
24825
+ message: "\uC30D\uC810\uC740 \uC55E\uB9D0\uC5D0 \uBD99\uC774\uACE0 \uB4A4\uB294 \uD55C \uCE78 \uB744\uC6C0",
24826
+ suggest: "\uC608) \uC6D0\uC7A5: \uAE40\uAC11\uB3D9"
24827
+ }
24828
+ ];
24829
+ function lintGongmunText(text) {
24830
+ const findings = [];
24831
+ let inFence = false;
24832
+ const lines = text.split(/\r?\n/);
24833
+ for (let i = 0; i < lines.length; i++) {
24834
+ const line = lines[i];
24835
+ if (/^\s*(```|~~~)/.test(line)) {
24836
+ inFence = !inFence;
24837
+ continue;
24838
+ }
24839
+ if (inFence) continue;
24840
+ for (const r of RULES) {
24841
+ r.pattern.lastIndex = 0;
24842
+ for (const m of line.matchAll(r.pattern)) {
24843
+ findings.push({
24844
+ line: i + 1,
24845
+ match: m[0].trim(),
24846
+ rule: r.code,
24847
+ severity: r.severity,
24848
+ message: r.message,
24849
+ suggest: r.suggest
24850
+ });
24851
+ }
24852
+ }
24853
+ }
24854
+ return findings;
24855
+ }
24856
+ function gongmunLintWarnings(text, limit = 10) {
24857
+ const findings = lintGongmunText(text);
24858
+ const shown = findings.slice(0, limit).map(
24859
+ (f) => `\uD45C\uAE30\uBC95 [${f.rule}] L${f.line} "${f.match}" \u2014 ${f.message}${f.suggest ? ` (${f.suggest})` : ""}`
24860
+ );
24861
+ if (findings.length > limit) shown.push(`\uD45C\uAE30\uBC95 \uACBD\uACE0 ${findings.length - limit}\uAC74 \uB354 \uC788\uC74C \u2014 kordoc lint\uB85C \uC804\uCCB4 \uD655\uC778`);
24862
+ return shown;
24863
+ }
24864
+
24707
24865
  // src/roundtrip/patcher.ts
24708
24866
  import JSZip10 from "jszip";
24709
24867
 
@@ -28393,7 +28551,7 @@ async function parseHwp(buffer, options) {
28393
28551
  async function parsePdf(buffer, options) {
28394
28552
  let parsePdfDocument;
28395
28553
  try {
28396
- const mod = await import("./parser-NXHY37WH.js");
28554
+ const mod = await import("./parser-G433UZMX.js");
28397
28555
  parsePdfDocument = mod.parsePdfDocument;
28398
28556
  } catch {
28399
28557
  return {
@@ -28508,6 +28666,7 @@ export {
28508
28666
  fillWithUniqueGuard,
28509
28667
  fitRatioForFewerLines,
28510
28668
  formatFillValue,
28669
+ gongmunLintWarnings,
28511
28670
  hwpxToProfile,
28512
28671
  inferFieldType,
28513
28672
  isHwpxFile,
@@ -28516,6 +28675,7 @@ export {
28516
28675
  isOldHwpFile,
28517
28676
  isPdfFile,
28518
28677
  isZipFile,
28678
+ lintGongmunText,
28519
28679
  markdownToHwpx,
28520
28680
  markdownToPdf,
28521
28681
  measureTextWidth,