kordoc 4.19.2 → 4.20.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.
- package/NOTICE +25 -0
- package/README.md +2 -1
- package/THIRD_PARTY/hwp-auto-docfit.LICENSE +23 -0
- package/dist/{-3JIHF2AL.js → -TTRGLREH.js} +11 -11
- package/dist/{chunk-I2TBI7PN.js → chunk-2U475PJF.js} +2 -2
- package/dist/{chunk-R2JM2HOJ.js → chunk-4DIMX2KU.js} +4 -4
- package/dist/{chunk-GNUGD4RO.js → chunk-4WOG2SLT.js} +2 -2
- package/dist/{chunk-QZ63HXEG.js → chunk-527NBMUE.js} +6 -6
- package/dist/{chunk-PWQ2BVAT.js → chunk-7H5GT7HM.js} +22 -3
- package/dist/{chunk-PWQ2BVAT.js.map → chunk-7H5GT7HM.js.map} +1 -1
- package/dist/{chunk-T2DZTOGG.cjs → chunk-AXQDWE42.cjs} +12 -12
- package/dist/{chunk-T2DZTOGG.cjs.map → chunk-AXQDWE42.cjs.map} +1 -1
- package/dist/{chunk-ORGWGR4K.cjs → chunk-CJFIHH2N.cjs} +6 -6
- package/dist/{chunk-ORGWGR4K.cjs.map → chunk-CJFIHH2N.cjs.map} +1 -1
- package/dist/{chunk-XPM7WC54.js → chunk-CKFKC22O.js} +4 -4
- package/dist/{chunk-JK7N4R4K.js → chunk-CPZ2LLZA.js} +2 -2
- package/dist/{chunk-6ZBORHFB.js → chunk-CS2VWXDS.js} +2 -2
- package/dist/{chunk-EUCBMW6C.js → chunk-DPD2L5UK.js} +3 -3
- package/dist/{chunk-AOVMOCTK.js → chunk-GHAJHXYA.js} +2 -2
- package/dist/{chunk-4CS7XE2Q.js → chunk-JYV3XQYW.js} +2 -2
- package/dist/{chunk-MZ6XNFZL.cjs → chunk-K5GCIB2I.cjs} +2 -2
- package/dist/{chunk-MZ6XNFZL.cjs.map → chunk-K5GCIB2I.cjs.map} +1 -1
- package/dist/{chunk-5PGOZBEQ.cjs → chunk-KDCPBF4X.cjs} +22 -22
- package/dist/{chunk-5PGOZBEQ.cjs.map → chunk-KDCPBF4X.cjs.map} +1 -1
- package/dist/{chunk-NV46IWKF.js → chunk-LQ4IDHET.js} +234 -24
- package/dist/chunk-LQ4IDHET.js.map +1 -0
- package/dist/{chunk-U6AJU2VD.js → chunk-MNPPMJBN.js} +2 -2
- package/dist/{chunk-J75TVRJB.js → chunk-MXADBQ7E.js} +2 -2
- package/dist/{chunk-B4R443MV.js → chunk-NVBX2NKZ.js} +2 -2
- package/dist/{chunk-TM4T4NYJ.js → chunk-OX5FA2MU.js} +2 -2
- package/dist/{chunk-WQAGDJIP.cjs → chunk-RMDFN5ZF.cjs} +3 -3
- package/dist/{chunk-WQAGDJIP.cjs.map → chunk-RMDFN5ZF.cjs.map} +1 -1
- package/dist/{chunk-QYJPU4FZ.js → chunk-TM7IOP4P.js} +2 -2
- package/dist/{chunk-OKHKEYTL.js → chunk-URQM5TBD.js} +2 -2
- package/dist/{chunk-VO22VAR3.js → chunk-VVKMBLG2.js} +2 -2
- package/dist/{chunk-JXJDXDOU.js → chunk-WLNEQGQC.js} +3 -3
- package/dist/{chunk-56ZWAREM.js → chunk-WLWWODBQ.js} +2 -2
- package/dist/{chunk-RVBQT2PD.cjs → chunk-WWOU6ZQ3.cjs} +3 -3
- package/dist/{chunk-RVBQT2PD.cjs.map → chunk-WWOU6ZQ3.cjs.map} +1 -1
- package/dist/{chunk-AKSCTGKG.js → chunk-X5YHJHWB.js} +2 -2
- package/dist/chunks-3NIFJFRQ.js +10 -0
- package/dist/cli.js +366 -44
- package/dist/cli.js.map +1 -1
- package/dist/{formula-STIFIS57.js → formula-D6GGALWI.js} +3 -3
- package/dist/{formula-RNATQ3FQ.cjs → formula-FE6LEZTT.cjs} +9 -9
- package/dist/{formula-RNATQ3FQ.cjs.map → formula-FE6LEZTT.cjs.map} +1 -1
- package/dist/{formula-RSTTP626.js → formula-KWOAYEOG.js} +4 -4
- package/dist/{image-ocr-XKSAEEXP.js → image-ocr-XDODV43Y.js} +9 -9
- package/dist/{image-ocr-7L5TBBPA.cjs → image-ocr-XGGIYCZ2.cjs} +15 -15
- package/dist/{image-ocr-7L5TBBPA.cjs.map → image-ocr-XGGIYCZ2.cjs.map} +1 -1
- package/dist/{image-ocr-R6ZDFBAN.js → image-ocr-YYTFOUSY.js} +6 -6
- package/dist/index.cjs +682 -459
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +242 -19
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +22 -22
- package/dist/{model-bundle-NGTKULP7.js → model-bundle-WPFLIKYM.js} +5 -5
- package/dist/{models-QOSU5SYL.js → models-WJ5QYELV.js} +5 -5
- package/dist/{parser-AIQBU7FS.cjs → parser-6P5ZRWXG.cjs} +132 -132
- package/dist/{parser-AIQBU7FS.cjs.map → parser-6P5ZRWXG.cjs.map} +1 -1
- package/dist/{parser-5GUEP6EK.js → parser-LY67CB7A.js} +8 -8
- package/dist/{parser-3G4G6SST.js → parser-QKLUIMHG.js} +11 -11
- package/dist/pdf-ocr-6L4EDRVO.js +20 -0
- package/dist/pdf-ocr-J7DHJJ3I.js +16 -0
- package/dist/pdf-ocr-MODN6GBT.cjs +17 -0
- package/dist/{pdf-ocr-7OAFUDM4.cjs.map → pdf-ocr-MODN6GBT.cjs.map} +1 -1
- package/dist/{profile-io-ZVNMDLLS.js → profile-io-I4ORJOE4.js} +3 -3
- package/dist/{rasterize-Y2RLMVCL.js → rasterize-MZU5LIA5.js} +3 -3
- package/dist/{redact-doc-PNRDDBI3.js → redact-doc-NEUEBXFH.js} +11 -11
- package/dist/{render-S3ALARVO.js → render-IL2DYXDP.js} +7 -7
- package/dist/seal-O5DKZGTR.js +10 -0
- package/dist/{setup-JH66VSFZ.js → setup-PGOCB7YG.js} +3 -3
- package/dist/visual-PYV4ZMLH.js +20 -0
- package/dist/{watch-KMB2FTOD.js → watch-2F5AMHCW.js} +12 -12
- package/package.json +1 -1
- package/dist/chunk-NV46IWKF.js.map +0 -1
- package/dist/chunks-2EC2R7PY.js +0 -10
- package/dist/pdf-ocr-7OAFUDM4.cjs +0 -17
- package/dist/pdf-ocr-DAXS5RX4.js +0 -16
- package/dist/pdf-ocr-R3VGX6F2.js +0 -20
- package/dist/seal-EJWPS5VK.js +0 -10
- package/dist/visual-GDXMKDVY.js +0 -20
- /package/dist/{-3JIHF2AL.js.map → -TTRGLREH.js.map} +0 -0
- /package/dist/{chunk-I2TBI7PN.js.map → chunk-2U475PJF.js.map} +0 -0
- /package/dist/{chunk-R2JM2HOJ.js.map → chunk-4DIMX2KU.js.map} +0 -0
- /package/dist/{chunk-GNUGD4RO.js.map → chunk-4WOG2SLT.js.map} +0 -0
- /package/dist/{chunk-QZ63HXEG.js.map → chunk-527NBMUE.js.map} +0 -0
- /package/dist/{chunk-XPM7WC54.js.map → chunk-CKFKC22O.js.map} +0 -0
- /package/dist/{chunk-JK7N4R4K.js.map → chunk-CPZ2LLZA.js.map} +0 -0
- /package/dist/{chunk-6ZBORHFB.js.map → chunk-CS2VWXDS.js.map} +0 -0
- /package/dist/{chunk-EUCBMW6C.js.map → chunk-DPD2L5UK.js.map} +0 -0
- /package/dist/{chunk-AOVMOCTK.js.map → chunk-GHAJHXYA.js.map} +0 -0
- /package/dist/{chunk-4CS7XE2Q.js.map → chunk-JYV3XQYW.js.map} +0 -0
- /package/dist/{chunk-U6AJU2VD.js.map → chunk-MNPPMJBN.js.map} +0 -0
- /package/dist/{chunk-J75TVRJB.js.map → chunk-MXADBQ7E.js.map} +0 -0
- /package/dist/{chunk-B4R443MV.js.map → chunk-NVBX2NKZ.js.map} +0 -0
- /package/dist/{chunk-TM4T4NYJ.js.map → chunk-OX5FA2MU.js.map} +0 -0
- /package/dist/{chunk-QYJPU4FZ.js.map → chunk-TM7IOP4P.js.map} +0 -0
- /package/dist/{chunk-OKHKEYTL.js.map → chunk-URQM5TBD.js.map} +0 -0
- /package/dist/{chunk-VO22VAR3.js.map → chunk-VVKMBLG2.js.map} +0 -0
- /package/dist/{chunk-JXJDXDOU.js.map → chunk-WLNEQGQC.js.map} +0 -0
- /package/dist/{chunk-56ZWAREM.js.map → chunk-WLWWODBQ.js.map} +0 -0
- /package/dist/{chunk-AKSCTGKG.js.map → chunk-X5YHJHWB.js.map} +0 -0
- /package/dist/{chunks-2EC2R7PY.js.map → chunks-3NIFJFRQ.js.map} +0 -0
- /package/dist/{formula-STIFIS57.js.map → formula-D6GGALWI.js.map} +0 -0
- /package/dist/{formula-RSTTP626.js.map → formula-KWOAYEOG.js.map} +0 -0
- /package/dist/{image-ocr-XKSAEEXP.js.map → image-ocr-XDODV43Y.js.map} +0 -0
- /package/dist/{image-ocr-R6ZDFBAN.js.map → image-ocr-YYTFOUSY.js.map} +0 -0
- /package/dist/{model-bundle-NGTKULP7.js.map → model-bundle-WPFLIKYM.js.map} +0 -0
- /package/dist/{models-QOSU5SYL.js.map → models-WJ5QYELV.js.map} +0 -0
- /package/dist/{parser-5GUEP6EK.js.map → parser-LY67CB7A.js.map} +0 -0
- /package/dist/{parser-3G4G6SST.js.map → parser-QKLUIMHG.js.map} +0 -0
- /package/dist/{pdf-ocr-DAXS5RX4.js.map → pdf-ocr-6L4EDRVO.js.map} +0 -0
- /package/dist/{pdf-ocr-R3VGX6F2.js.map → pdf-ocr-J7DHJJ3I.js.map} +0 -0
- /package/dist/{profile-io-ZVNMDLLS.js.map → profile-io-I4ORJOE4.js.map} +0 -0
- /package/dist/{rasterize-Y2RLMVCL.js.map → rasterize-MZU5LIA5.js.map} +0 -0
- /package/dist/{redact-doc-PNRDDBI3.js.map → redact-doc-NEUEBXFH.js.map} +0 -0
- /package/dist/{render-S3ALARVO.js.map → render-IL2DYXDP.js.map} +0 -0
- /package/dist/{seal-EJWPS5VK.js.map → seal-O5DKZGTR.js.map} +0 -0
- /package/dist/{setup-JH66VSFZ.js.map → setup-PGOCB7YG.js.map} +0 -0
- /package/dist/{visual-GDXMKDVY.js.map → visual-PYV4ZMLH.js.map} +0 -0
- /package/dist/{watch-KMB2FTOD.js.map → watch-2F5AMHCW.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -1284,6 +1284,8 @@ declare function unknownFontWarnings(fonts: Record<string, string | undefined>):
|
|
|
1284
1284
|
* 외래어·차별 표현·"끝." 누락 6룰 보강 (pyhwpxlib Gongmun 검사 항목 대조). 검사는 조언용이다 — 생성은 막지 않고
|
|
1285
1285
|
* 경고만 낸다 (A2 폰트경고와 같은 원칙). 별도 CLI `kordoc lint`는 error 시 exit 1.
|
|
1286
1286
|
* v4.13.0: 하이픈 날짜(2026-07-18) 룰, 금액 한글 병기·하이픈 날짜는 실제 변환값을 제안.
|
|
1287
|
+
* v4.20.0: 기간 대시·요일·금액 한글 불일치·"끝."/붙임 2타·공공언어·맞춤법 룰 — 규칙 착안과 공공언어·맞춤법 낱말표·
|
|
1288
|
+
* 조사 경계식은 hwp-auto-docfit(MIT, haijun93 마포구청 공혁준, THIRD_PARTY/hwp-auto-docfit.LICENSE)에서 옮겼다.
|
|
1287
1289
|
*/
|
|
1288
1290
|
interface GongmunLintFinding {
|
|
1289
1291
|
/** 1-based 줄 번호 */
|
|
@@ -1299,9 +1301,12 @@ interface GongmunLintFinding {
|
|
|
1299
1301
|
/**
|
|
1300
1302
|
* 텍스트(마크다운 포함) 표기법 검수. 마크다운 펜스 코드블록(``` ~ ```) 안은
|
|
1301
1303
|
* 건너뛴다 — 코드·URL이 날짜/쌍점 규칙에 오탐되는 것 방지.
|
|
1304
|
+
* opts.document: 완성 원고 검사(문서 단위 규칙 포함). opts.parsed: HWPX·HWP 등을 파싱한 글 — 연속 공백이 접히고
|
|
1305
|
+
* 원문 굵게가 `**` 로 옮겨져 원고 전용 규칙("끝."·붙임 2타, 강조 남발)은 판정할 수 없다(원문 "붙임 … 1부. 끝." 이 한 칸)
|
|
1302
1306
|
*/
|
|
1303
1307
|
declare function lintGongmunText(text: string, opts?: {
|
|
1304
1308
|
document?: boolean;
|
|
1309
|
+
parsed?: boolean;
|
|
1305
1310
|
}): GongmunLintFinding[];
|
|
1306
1311
|
/** 검수 결과를 사람이 읽는 경고 문자열 배열로 — generate 경고 채널(A2와 동일)용 */
|
|
1307
1312
|
declare function gongmunLintWarnings(text: string, limit?: number): string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -1284,6 +1284,8 @@ declare function unknownFontWarnings(fonts: Record<string, string | undefined>):
|
|
|
1284
1284
|
* 외래어·차별 표현·"끝." 누락 6룰 보강 (pyhwpxlib Gongmun 검사 항목 대조). 검사는 조언용이다 — 생성은 막지 않고
|
|
1285
1285
|
* 경고만 낸다 (A2 폰트경고와 같은 원칙). 별도 CLI `kordoc lint`는 error 시 exit 1.
|
|
1286
1286
|
* v4.13.0: 하이픈 날짜(2026-07-18) 룰, 금액 한글 병기·하이픈 날짜는 실제 변환값을 제안.
|
|
1287
|
+
* v4.20.0: 기간 대시·요일·금액 한글 불일치·"끝."/붙임 2타·공공언어·맞춤법 룰 — 규칙 착안과 공공언어·맞춤법 낱말표·
|
|
1288
|
+
* 조사 경계식은 hwp-auto-docfit(MIT, haijun93 마포구청 공혁준, THIRD_PARTY/hwp-auto-docfit.LICENSE)에서 옮겼다.
|
|
1287
1289
|
*/
|
|
1288
1290
|
interface GongmunLintFinding {
|
|
1289
1291
|
/** 1-based 줄 번호 */
|
|
@@ -1299,9 +1301,12 @@ interface GongmunLintFinding {
|
|
|
1299
1301
|
/**
|
|
1300
1302
|
* 텍스트(마크다운 포함) 표기법 검수. 마크다운 펜스 코드블록(``` ~ ```) 안은
|
|
1301
1303
|
* 건너뛴다 — 코드·URL이 날짜/쌍점 규칙에 오탐되는 것 방지.
|
|
1304
|
+
* opts.document: 완성 원고 검사(문서 단위 규칙 포함). opts.parsed: HWPX·HWP 등을 파싱한 글 — 연속 공백이 접히고
|
|
1305
|
+
* 원문 굵게가 `**` 로 옮겨져 원고 전용 규칙("끝."·붙임 2타, 강조 남발)은 판정할 수 없다(원문 "붙임 … 1부. 끝." 이 한 칸)
|
|
1302
1306
|
*/
|
|
1303
1307
|
declare function lintGongmunText(text: string, opts?: {
|
|
1304
1308
|
document?: boolean;
|
|
1309
|
+
parsed?: boolean;
|
|
1305
1310
|
}): GongmunLintFinding[];
|
|
1306
1311
|
/** 검수 결과를 사람이 읽는 경고 문자열 배열로 — generate 경고 채널(A2와 동일)용 */
|
|
1307
1312
|
declare function gongmunLintWarnings(text: string, limit?: number): string[];
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
blocksToPages,
|
|
3
3
|
inlineImagesIntoMarkdown
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MNPPMJBN.js";
|
|
5
5
|
import {
|
|
6
6
|
parsePageRange
|
|
7
7
|
} from "./chunk-V4BH5SRQ.js";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
tidyScriptTags,
|
|
33
33
|
unframeLayoutTables,
|
|
34
34
|
wrapScript
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-X5YHJHWB.js";
|
|
36
36
|
import {
|
|
37
37
|
KordocError,
|
|
38
38
|
OPTIONAL_DEP_INSTALL_HINT,
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
toArrayBuffer,
|
|
51
51
|
unescapeHtml,
|
|
52
52
|
unzipLimitBytes
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-MXADBQ7E.js";
|
|
54
54
|
|
|
55
55
|
// src/plain-markdown.ts
|
|
56
56
|
function toPlainMarkdown(md2) {
|
|
@@ -23669,6 +23669,24 @@ var EOCD_SIG = 101010256;
|
|
|
23669
23669
|
var CD_SIG = 33639248;
|
|
23670
23670
|
var LOCAL_SIG = 67324752;
|
|
23671
23671
|
var ZIP64_EOCD_LOC_SIG = 117853008;
|
|
23672
|
+
function readZipEntries(buf) {
|
|
23673
|
+
const { entries } = parseCentralDirectory(buf);
|
|
23674
|
+
const result = /* @__PURE__ */ new Map();
|
|
23675
|
+
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
23676
|
+
for (const e of entries) {
|
|
23677
|
+
const dataStart = localDataStart(view, e.localOffset);
|
|
23678
|
+
result.set(e.name, { method: e.method, compData: buf.subarray(dataStart, dataStart + e.compSize) });
|
|
23679
|
+
}
|
|
23680
|
+
return result;
|
|
23681
|
+
}
|
|
23682
|
+
function localDataStart(view, localOffset) {
|
|
23683
|
+
if (view.getUint32(localOffset, true) !== LOCAL_SIG) {
|
|
23684
|
+
throw new KordocError("ZIP \uB85C\uCEEC \uD5E4\uB354 \uC2DC\uADF8\uB2C8\uCC98 \uBD88\uC77C\uCE58");
|
|
23685
|
+
}
|
|
23686
|
+
const nameLen = view.getUint16(localOffset + 26, true);
|
|
23687
|
+
const extraLen = view.getUint16(localOffset + 28, true);
|
|
23688
|
+
return localOffset + 30 + nameLen + extraLen;
|
|
23689
|
+
}
|
|
23672
23690
|
function copyBytes(buf, start, end) {
|
|
23673
23691
|
return new Uint8Array(buf.subarray(start, end));
|
|
23674
23692
|
}
|
|
@@ -27539,7 +27557,7 @@ function parseInlineMarkdown(text) {
|
|
|
27539
27557
|
}
|
|
27540
27558
|
return spans;
|
|
27541
27559
|
}
|
|
27542
|
-
var INLINE_REGEX = /(`[^`]+`|\*{3}(?:[^*]|\*(?!\*\*))
|
|
27560
|
+
var INLINE_REGEX = /(`[^`]+`|\*{3}(?=\S)(?:[^*]|\*(?!\*\*))+(?<=\S)\*{3}|\*{2}(?=\S)(?:[^*]|\*(?!\*))+(?<=\S)\*{2}|\*(?=\S)(?:[^*]|\*{2}(?:[^*]|\*(?!\*))+\*{2})+(?<=\S)\*|(?<![^\s\p{P}\p{S}\x00])(?<!_)_{2}(?=\S)[^_]+(?<=\S)_{2}(?!_)(?![^\s\p{P}\p{S}\x00])|(?<![^\s\p{P}\p{S}\x00])(?<!_)_(?=\S)[^_]+(?<=\S)_(?!_)(?![^\s\p{P}\p{S}\x00]))/gu;
|
|
27543
27561
|
function emphasisSpans(text, depth = 0) {
|
|
27544
27562
|
const spans = [];
|
|
27545
27563
|
let lastIdx = 0;
|
|
@@ -31316,7 +31334,7 @@ function markerRunXml(marker, charPr2, lay) {
|
|
|
31316
31334
|
var NBSP = "\xA0";
|
|
31317
31335
|
var OPENER_BEFORE = /[\s(\[{>「『〈《【〔‘“·/:,~—–-]/u;
|
|
31318
31336
|
function bindSpaces(s) {
|
|
31319
|
-
return s.replace(/(\d{4}|['’]\d{2})\. (\d{1,2})\. (\d{1,2})\.(?!\d)/g, `$1.${NBSP}$2.${NBSP}$3.`).replace(/(\d{4}|['’]\d{2})\. (\d{1,2})\.(?!\d)/g, `$1.${NBSP}$2.`).replace(/(?<![\d.])(\d{1,2})\. (\d{1,2})\.(?!\d)/g, `$1.${NBSP}$2.`).replace(/(\d{1,2}:\d{2}) ([~∼-]) (\d{1,2}:\d{2})/g, `$1${NBSP}$2${NBSP}$3`).replace(/(\d(?:조|억|만|천)?) 원/g, `$1${NBSP}\uC6D0`);
|
|
31337
|
+
return s.replace(/(\d{4}|['’]\d{2})\. (\d{1,2})\. (\d{1,2})\.(?!\d)/g, `$1.${NBSP}$2.${NBSP}$3.`).replace(/(\d{4}|['’]\d{2})\. (\d{1,2})\.(?!\d)/g, `$1.${NBSP}$2.`).replace(/(?<![\d.])(\d{1,2})\. (\d{1,2})\.(?!\d)/g, `$1.${NBSP}$2.`).replace(/(\d{1,2}:\d{2}) ([~∼-]) (\d{1,2}:\d{2})/g, `$1${NBSP}$2${NBSP}$3`).replace(/(\d(?:조|억|만|천)?) 원/g, `$1${NBSP}\uC6D0`).replace(/(?<=[가-힣A-Za-z0-9))*.]) 등(?=(?:으로서|으로써|으로|에서|에게|까지|부터|이며|이고|이다|이나|이라|과|와|을|를|은|는|이|가|의|에|도|만)?[,,.))」』’”]*(?:\s|$))/g, `${NBSP}\uB4F1`).replace(/(?<=[가-힣A-Za-z]) (?=\d+(?:\.\d+)?[,,](?:\s|$))/g, NBSP).replace(/(?<=\d[,,][ \u00a0]+[가-힣A-Za-z]+) (?=\d+(?:\.\d+)?[^\s,,\d]*(?:\s|$))/g, NBSP);
|
|
31320
31338
|
}
|
|
31321
31339
|
function neighbor(s, i, dir) {
|
|
31322
31340
|
for (let k = i + dir; k >= 0 && k < s.length; k += dir) if (!/[*_~]/.test(s[k])) return s[k];
|
|
@@ -32076,12 +32094,39 @@ function legalDepthOf(marker) {
|
|
|
32076
32094
|
return 6;
|
|
32077
32095
|
}
|
|
32078
32096
|
|
|
32097
|
+
// src/hwpx/paste-clean.ts
|
|
32098
|
+
var INVISIBLE = /[\u200b\u200c\u2060\ufeff]/g;
|
|
32099
|
+
var CITATION_TAG = /[ \t]?【[^【】†\n]*†[^【】\n]*】/g;
|
|
32100
|
+
var ESCAPED_BOLD = /(?<![\\*])\\\*\\\*(?=[^\s\\*<])([^\\\n<]+?)(?<=[^\s\\*>])\\\*\\\*(?!\\\*)/g;
|
|
32101
|
+
function cleanPastedMarkdown(md2) {
|
|
32102
|
+
let removed = 0;
|
|
32103
|
+
let fence = null;
|
|
32104
|
+
const count = (re, s, to) => s.replace(re, (...a) => {
|
|
32105
|
+
removed++;
|
|
32106
|
+
return typeof to === "string" ? to : to(...a);
|
|
32107
|
+
});
|
|
32108
|
+
const lines = md2.split("\n").map((line) => {
|
|
32109
|
+
const f = line.match(/^\s*(```+|~~~+)/);
|
|
32110
|
+
if (f) {
|
|
32111
|
+
fence = fence === null ? f[1][0] : fence === f[1][0] ? null : fence;
|
|
32112
|
+
return line;
|
|
32113
|
+
}
|
|
32114
|
+
if (fence !== null) return line;
|
|
32115
|
+
let out = count(INVISIBLE, line, "");
|
|
32116
|
+
out = count(CITATION_TAG, out, "");
|
|
32117
|
+
return count(ESCAPED_BOLD, out, (_m, inner) => `**${inner}**`);
|
|
32118
|
+
});
|
|
32119
|
+
return { md: lines.join("\n"), removed };
|
|
32120
|
+
}
|
|
32121
|
+
|
|
32079
32122
|
// src/hwpx/generator.ts
|
|
32080
32123
|
async function markdownToHwpx(markdown, options) {
|
|
32081
32124
|
const theme = resolveTheme(options?.theme);
|
|
32082
32125
|
const gongmun = options?.gongmun ? resolveGongmun(options.gongmun) : null;
|
|
32083
32126
|
const page = resolvePage(options?.page);
|
|
32084
|
-
const
|
|
32127
|
+
const pasted = cleanPastedMarkdown(markdown);
|
|
32128
|
+
if (pasted.removed > 0) options?.warnings?.push(`\uBD99\uC5EC\uB123\uAE30 \uD754\uC801 ${pasted.removed}\uAC1C \uC815\uB9AC (\uBCF4\uC774\uC9C0 \uC54A\uB294 \uAE00\uC790\xB7\uC778\uC6A9 \uD45C\uC2DC\xB7\uC774\uC2A4\uCF00\uC774\uD504\uB41C \uAD75\uAC8C)`);
|
|
32129
|
+
const { md: md2, defs } = extractFootnoteDefs(pasted.md);
|
|
32085
32130
|
beginInlineDoc(defs);
|
|
32086
32131
|
try {
|
|
32087
32132
|
if (gongmun && usesV5Engine(gongmun.preset)) {
|
|
@@ -33327,14 +33372,71 @@ var DISCRIM_FIXES = [
|
|
|
33327
33372
|
["\uADC0\uBA38\uAC70\uB9AC", "\uCCAD\uAC01\uC7A5\uC560\uC778"]
|
|
33328
33373
|
];
|
|
33329
33374
|
var DISCRIM_RE = new RegExp(DISCRIM_FIXES.map(([w]) => w).sort((a, b) => b.length - a.length).join("|"), "g");
|
|
33375
|
+
var PLAIN_FIXES = [
|
|
33376
|
+
["\uAE08\uBC88", "\uC774\uBC88"],
|
|
33377
|
+
["\uAE08\uC77C", "\uC624\uB298"],
|
|
33378
|
+
["\uBA85\uC77C", "\uB0B4\uC77C"],
|
|
33379
|
+
["\uC791\uC77C", "\uC5B4\uC81C"],
|
|
33380
|
+
["\uC775\uC77C", "\uB2E4\uC74C \uB0A0"],
|
|
33381
|
+
["\uACE0\uC218\uBD80\uC9C0", "\uB454\uCE58"],
|
|
33382
|
+
["\uBD88\uCCA0\uC8FC\uC57C", "\uBC24\uB0AE\uC5C6\uC774"]
|
|
33383
|
+
];
|
|
33384
|
+
var SPELLING_FIXES = [
|
|
33385
|
+
["\uBA87\uC77C", "\uBA70\uCE60"],
|
|
33386
|
+
["\uAC2F\uC218", "\uAC1C\uC218"],
|
|
33387
|
+
["\uB42C\uC2B5\uB2C8\uB2E4", "\uB410\uC2B5\uB2C8\uB2E4"],
|
|
33388
|
+
["\uB42C\uB2E4", "\uB410\uB2E4"],
|
|
33389
|
+
["\uC660\uB9CC\uD558\uBA74", "\uC6EC\uB9CC\uD558\uBA74"],
|
|
33390
|
+
["\uC5B4\uC758\uC5C6\uB2E4", "\uC5B4\uC774\uC5C6\uB2E4"],
|
|
33391
|
+
["\uB418\uC694", "\uB3FC\uC694"]
|
|
33392
|
+
];
|
|
33393
|
+
var wordRe = (fixes) => new RegExp(
|
|
33394
|
+
"(?<![\uAC00-\uD7A3A-Za-z0-9])(?:" + fixes.map(([w]) => w).sort((a, b) => b.length - a.length).join("|") + ")(?=(?:\uC73C\uB85C|\uBD80\uD130|\uAE4C\uC9C0|\uB3D9\uC548|\uC774\uB098|\uC740|\uB294|\uC774|\uAC00|\uC744|\uB97C|\uC5D0|\uC758|\uB3C4|\uB9CC|\uB85C|\uACFC|\uC640|\uAC04)?(?![\uAC00-\uD7A3A-Za-z0-9]))",
|
|
33395
|
+
"g"
|
|
33396
|
+
);
|
|
33397
|
+
var PLAIN_RE = wordRe(PLAIN_FIXES);
|
|
33398
|
+
var SPELLING_RE = wordRe(SPELLING_FIXES);
|
|
33399
|
+
var WEEKDAYS = "\uC77C\uC6D4\uD654\uC218\uBAA9\uAE08\uD1A0";
|
|
33400
|
+
function weekdayCheck(m) {
|
|
33401
|
+
const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
33402
|
+
const date = new Date(y, mo - 1, d);
|
|
33403
|
+
if (date.getFullYear() !== y || date.getMonth() !== mo - 1 || date.getDate() !== d) return null;
|
|
33404
|
+
const right = WEEKDAYS[date.getDay()];
|
|
33405
|
+
return right === m[4] ? null : `${m[0].trim()} \u2192 ${y}. ${mo}. ${d}.(${right})`;
|
|
33406
|
+
}
|
|
33407
|
+
var HANGUL_DIGIT = { \uC77C: 1, \uC774: 2, \uC0BC: 3, \uC0AC: 4, \uC624: 5, \uC721: 6, \uCE60: 7, \uD314: 8, \uAD6C: 9, \uC601: 0 };
|
|
33408
|
+
var HANGUL_SMALL = { \uC2ED: 10, \uBC31: 100, \uCC9C: 1e3 };
|
|
33409
|
+
var HANGUL_BIG = { \uB9CC: 1e4, \uC5B5: 1e8, \uC870: 1e12 };
|
|
33410
|
+
function hangulToNumber(s) {
|
|
33411
|
+
let total = 0, section = 0, digit = 0;
|
|
33412
|
+
for (const ch of s) {
|
|
33413
|
+
if (ch in HANGUL_DIGIT) digit = HANGUL_DIGIT[ch];
|
|
33414
|
+
else if (ch in HANGUL_SMALL) {
|
|
33415
|
+
section += (digit || 1) * HANGUL_SMALL[ch];
|
|
33416
|
+
digit = 0;
|
|
33417
|
+
} else if (ch in HANGUL_BIG) {
|
|
33418
|
+
total += (section + digit || 1) * HANGUL_BIG[ch];
|
|
33419
|
+
section = 0;
|
|
33420
|
+
digit = 0;
|
|
33421
|
+
} else return null;
|
|
33422
|
+
}
|
|
33423
|
+
return total + section + digit;
|
|
33424
|
+
}
|
|
33425
|
+
function moneyHangulCheck(m) {
|
|
33426
|
+
const n = Number(m[1].replace(/,/g, ""));
|
|
33427
|
+
const h = hangulToNumber(m[2].replace(/\s/g, ""));
|
|
33428
|
+
if (!Number.isFinite(n) || h === null || h === n) return null;
|
|
33429
|
+
return `\uAE08${m[1]}\uC6D0(\uAE08${hangulAmount(n)}\uC6D0)`;
|
|
33430
|
+
}
|
|
33330
33431
|
var RULES = [
|
|
33331
33432
|
// 날짜 ─ 온점 뒤 한 칸, 0 패딩 금지, 연도 4자리, 끝 마침표
|
|
33332
33433
|
// 법령 연혁 표기 `<개정 2012.2.14>`·`삭제 <2016.2.29.>`·`[시행일:2017.9.8.]` 는 법제처 정본 형식(별지서식 표제) —
|
|
33333
33434
|
// 편람 날짜 규칙 대상이 아니다 (v4.12.3: 서식 595건 DATE 156건 전부 이 꼴, 기안문 0건)
|
|
33435
|
+
// 날짜 모양이 시작되는 자리에서만 뒤쪽 검사 — 뒤쪽 검사(\s*)를 모든 자리에서 먼저 하면 긴 공백 줄에서 이차 폭주(2만 칸 0.7초)
|
|
33334
33436
|
{
|
|
33335
33437
|
code: "DATE_NO_SPACE",
|
|
33336
33438
|
severity: "error",
|
|
33337
|
-
pattern:
|
|
33439
|
+
pattern: /\b(?=\d{4}\.\d{1,2}\.\d)(?<!<(?:개정|신설|전문개정|전부개정|일부개정|제정)\s*)(?<!삭제\s*<)(?<!시행일\s*:\s*)\d{4}\.\d{1,2}\.\d{1,2}\.?/g,
|
|
33338
33440
|
message: "\uB0A0\uC9DC \uC628\uC810 \uB4A4\uC5D0 \uD55C \uCE78\uC529 \uB744\uC6CC\uC57C \uD568",
|
|
33339
33441
|
suggest: "\uC608) 2025. 1. 6."
|
|
33340
33442
|
},
|
|
@@ -33381,7 +33483,7 @@ var RULES = [
|
|
|
33381
33483
|
code: "TIME_24H",
|
|
33382
33484
|
severity: "warning",
|
|
33383
33485
|
pattern: /(?<!\d)24\s*시(?!각)/g,
|
|
33384
|
-
message: "'24\uC2DC'\uBCF4\uB2E4 \
|
|
33486
|
+
message: "'24\uC2DC'\uBCF4\uB2E4 \uB2E4\uC74C \uB0A0 00:00 \uB610\uB294 '18:00\uAE4C\uC9C0' \uAD8C\uC7A5",
|
|
33385
33487
|
suggest: "\uC608) 18:00"
|
|
33386
33488
|
},
|
|
33387
33489
|
{
|
|
@@ -33392,10 +33494,11 @@ var RULES = [
|
|
|
33392
33494
|
suggest: "\uC608) 13:20"
|
|
33393
33495
|
},
|
|
33394
33496
|
// 금액 ─ '천원' 금지, 금+숫자 붙여쓰기
|
|
33497
|
+
// 숫자 런 첫머리에서만 시작 — 아니면 긴 숫자 줄에서 시작 자리마다 런을 다시 훑어 이차 폭주(숫자 5만 자 2.9초)
|
|
33395
33498
|
{
|
|
33396
33499
|
code: "MONEY_CHEONWON",
|
|
33397
33500
|
severity: "error",
|
|
33398
|
-
pattern:
|
|
33501
|
+
pattern: /(?<!\d)\d+\s*천\s*원/g,
|
|
33399
33502
|
message: "\uAE08\uC561\uC740 '\uCC9C\uC6D0'\uC73C\uB85C \uC904\uC774\uC9C0 \uC54A\uACE0 \uC544\uB77C\uBE44\uC544 \uC22B\uC790\uB85C",
|
|
33400
33503
|
suggest: "\uC608) 345,000\uC6D0"
|
|
33401
33504
|
},
|
|
@@ -33444,6 +33547,14 @@ var RULES = [
|
|
|
33444
33547
|
},
|
|
33445
33548
|
// ── 편람 보강 (v4.12.1) — pyhwpxlib Gongmun 검사·hwpx-skill gonmun_lint 대조로 빈 축 보충 ──
|
|
33446
33549
|
// 금액 한글 병기 — 규정 시행규칙 제2조: 아라비아 숫자 다음 괄호 안에 한글로 적는다
|
|
33550
|
+
// 한글 금액은 글자 묶음 사이에만 공백 — 공백 수량자가 겹치면 닫는 괄호 없는 긴 공백 줄에서 폭주했다(160칸 9.8초)
|
|
33551
|
+
{
|
|
33552
|
+
code: "MONEY_HANGUL_MISMATCH",
|
|
33553
|
+
severity: "error",
|
|
33554
|
+
pattern: /금\s?(\d[\d,]*)원\s*[((]\s*(?:금\s*)?([일이삼사오육칠팔구십백천만억조영]+(?:\s+[일이삼사오육칠팔구십백천만억조영]+)*)\s*원?\s*[))]/g,
|
|
33555
|
+
check: moneyHangulCheck,
|
|
33556
|
+
message: "\uBCD1\uAE30\uD55C \uD55C\uAE00 \uAE08\uC561\uC774 \uC22B\uC790\uC640 \uB2E4\uB984"
|
|
33557
|
+
},
|
|
33447
33558
|
{
|
|
33448
33559
|
code: "MONEY_NO_HANGUL",
|
|
33449
33560
|
severity: "warning",
|
|
@@ -33454,6 +33565,22 @@ var RULES = [
|
|
|
33454
33565
|
// 물결표 앞뒤 붙여쓰기 — 기간·범위는 "2. 20.∼2. 24." 처럼 붙여 씀. 숫자·날짜·시각 범위(앞은 숫자·
|
|
33455
33566
|
// 온점·괄호·년월일시분, 뒤는 숫자·연도 생략부호)에만 건다 — "기간( 부터 ~ 까지)" 기입란·"~ ※ 비고"·
|
|
33456
33567
|
// "~ 수리완료시까지" 처럼 낱말이 붙는 물결표는 범위 표기가 아니다 (v4.12.2, 별지서식 47→30·기안문 32→27 실측)
|
|
33568
|
+
// 기간·시각 범위의 대시 — 점 날짜(연·월·일)나 시:분 사이만 잡아 전화번호(02-123-4567)·조항(제4조-2)·코드(IT-2026-01)는
|
|
33569
|
+
// 걸리지 않는다. 자동 치환은 하지 않는다(명세 (d) 물결표 자동 치환 위험 — 검수만)
|
|
33570
|
+
{
|
|
33571
|
+
code: "RANGE_DASH",
|
|
33572
|
+
severity: "warning",
|
|
33573
|
+
pattern: /\d{2,4}\.\s*\d{1,2}\.\s*\d{1,2}\.?\s*[-–—]\s*(?:\d{2,4}\.\s*)?\d{1,2}\.\s*\d{1,2}\.?|(?<![\d:])\d{1,2}:\d{2}\s*[-–—]\s*\d{1,2}:\d{2}(?![\d:])/g,
|
|
33574
|
+
message: "\uAE30\uAC04\xB7\uC2DC\uAC01 \uBC94\uC704\uB294 \uBB3C\uACB0\uD45C(\u223C)\uB85C",
|
|
33575
|
+
suggest: "\uC608) 2026. 3. 1.\u223C6. 30., 14:00\u223C16:00"
|
|
33576
|
+
},
|
|
33577
|
+
{
|
|
33578
|
+
code: "WEEKDAY_MISMATCH",
|
|
33579
|
+
severity: "error",
|
|
33580
|
+
pattern: /(?<![\d.])(\d{4})\.\s*(\d{1,2})\.\s*(\d{1,2})\.?\s*\(([월화수목금토일])\)/g,
|
|
33581
|
+
check: weekdayCheck,
|
|
33582
|
+
message: "\uB0A0\uC9DC\uC640 \uC694\uC77C\uC774 \uB9DE\uC9C0 \uC54A\uC74C"
|
|
33583
|
+
},
|
|
33457
33584
|
{
|
|
33458
33585
|
code: "TILDE_SPACE",
|
|
33459
33586
|
severity: "warning",
|
|
@@ -33480,6 +33607,39 @@ var RULES = [
|
|
|
33480
33607
|
suggest: "\uC608) \uCF58\uD150\uCE20\xB7\uC560\uD50C\uB9AC\uCF00\uC774\uC158\xB7\uBA54\uC2DC\uC9C0\xB7\uC6CC\uD06C\uC20D\xB7\uC2A4\uCF00\uC904\xB7\uCF58\uC149\uD2B8"
|
|
33481
33608
|
},
|
|
33482
33609
|
// 차별·비하 표현 — 행안부 공문서 작성 지침·국립국어원 순화어
|
|
33610
|
+
// 표 줄은 건너뜀 — 법정 서식 라벨 칸("전일 지침 | 금일 지침")은 바꿀 대상이 아니다 (gate-fill·licbyl 801건 실측 7건 전부 표 라벨)
|
|
33611
|
+
{
|
|
33612
|
+
code: "PLAIN_LANGUAGE",
|
|
33613
|
+
severity: "warning",
|
|
33614
|
+
skipTable: true,
|
|
33615
|
+
pattern: PLAIN_RE,
|
|
33616
|
+
message: "\uACF5\uACF5\uC5B8\uC5B4 \uC26C\uC6B4 \uB9D0\uB85C \uBC14\uAFD4 \uC4F0\uAE30 \uAD8C\uC7A5 (\uD2C0\uB9B0 \uB9D0\uC740 \uC544\uB2D8)"
|
|
33617
|
+
},
|
|
33618
|
+
{
|
|
33619
|
+
code: "SPELLING_ERROR",
|
|
33620
|
+
severity: "warning",
|
|
33621
|
+
pattern: SPELLING_RE,
|
|
33622
|
+
message: "\uB9DE\uCDA4\uBC95 \uC624\uAE30"
|
|
33623
|
+
},
|
|
33624
|
+
// 끝 표시·붙임 표시 2타 (규칙 제2조제2항·편람) — 생성 경로는 official 프리셋이 스스로 맞춘다
|
|
33625
|
+
{
|
|
33626
|
+
code: "END_MARK_SPACE",
|
|
33627
|
+
severity: "warning",
|
|
33628
|
+
documentOnly: true,
|
|
33629
|
+
rawSource: true,
|
|
33630
|
+
pattern: /(?<=\.)(?:[ \t\u3000]?|[ \t\u3000]{3,})끝\.[ \t\u3000]*$/g,
|
|
33631
|
+
message: "'\uB05D.'\uC740 \uC55E \uAE00 \uB9C8\uCE68\uD45C\uC5D0\uC11C 2\uD0C0 \uB744\uC6C0",
|
|
33632
|
+
suggest: "\uC608) \u2026\uBC14\uB78D\uB2C8\uB2E4. \uB05D."
|
|
33633
|
+
},
|
|
33634
|
+
{
|
|
33635
|
+
code: "BUNIM_SPACE",
|
|
33636
|
+
severity: "warning",
|
|
33637
|
+
documentOnly: true,
|
|
33638
|
+
rawSource: true,
|
|
33639
|
+
pattern: /^[ \t\u3000]*붙임(?:[ \t\u3000]|[ \t\u3000]{3,})(?=[^ \t\u3000::])(?=.*(?<!\d)\d+\s*부\.(?:[ \t\u3000]*끝\.)?[ \t\u3000]*$)/g,
|
|
33640
|
+
message: "'\uBD99\uC784' \uB4A4\uB294 2\uD0C0 \uB744\uC6C0",
|
|
33641
|
+
suggest: "\uC608) \uBD99\uC784 \uACC4\uD68D\uC11C 1\uBD80. \uB05D."
|
|
33642
|
+
},
|
|
33483
33643
|
{
|
|
33484
33644
|
code: "DISCRIMINATORY_TERM",
|
|
33485
33645
|
severity: "warning",
|
|
@@ -33499,6 +33659,7 @@ var RULES = [
|
|
|
33499
33659
|
{
|
|
33500
33660
|
code: "AI_BOLD_OVERUSE",
|
|
33501
33661
|
severity: "warning",
|
|
33662
|
+
rawSource: true,
|
|
33502
33663
|
pattern: /(?:\*\*[^*\n]+\*\*[^*\n]*){3}/g,
|
|
33503
33664
|
message: "\uD55C \uC904\uC5D0 \uAC15\uC870(**) 3\uD68C \uC774\uC0C1 \u2014 \uAC15\uC870 \uB0A8\uBC1C\uC740 \uC0DD\uC131\uD615 AI \uBB38\uCCB4 \uD754\uC801",
|
|
33504
33665
|
suggest: "\uB9AC\uB4DC\uC5B4\xB7\uD575\uC2EC \uC218\uCE58 \uD55C \uACF3\uB9CC \uAC15\uC870"
|
|
@@ -33521,15 +33682,19 @@ function lintGongmunText(text, opts) {
|
|
|
33521
33682
|
const tableLine = /^\s*\|/.test(line) || /<t[dhr][\s>]/i.test(line);
|
|
33522
33683
|
for (const r of RULES) {
|
|
33523
33684
|
if (r.skipTable && tableLine) continue;
|
|
33685
|
+
if (r.documentOnly && !opts?.document) continue;
|
|
33686
|
+
if (r.rawSource && opts?.parsed) continue;
|
|
33524
33687
|
r.pattern.lastIndex = 0;
|
|
33525
33688
|
for (const m of line.matchAll(r.pattern)) {
|
|
33689
|
+
const checked = r.check ? r.check(m) : void 0;
|
|
33690
|
+
if (checked === null) continue;
|
|
33526
33691
|
findings.push({
|
|
33527
33692
|
line: i + 1,
|
|
33528
|
-
match: m[0].trim(),
|
|
33693
|
+
match: m[0].trim() || m[0],
|
|
33529
33694
|
rule: r.code,
|
|
33530
33695
|
severity: r.severity,
|
|
33531
33696
|
message: r.message,
|
|
33532
|
-
suggest: dictSuggest(r.code, m[0]) ?? r.suggest
|
|
33697
|
+
suggest: checked ?? dictSuggest(r.code, m[0]) ?? r.suggest
|
|
33533
33698
|
});
|
|
33534
33699
|
}
|
|
33535
33700
|
}
|
|
@@ -33553,7 +33718,7 @@ function dictSuggest(code, match) {
|
|
|
33553
33718
|
const d = m.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
|
|
33554
33719
|
if (d) return `${m} \u2192 ${d[1]}. ${Number(d[2])}. ${Number(d[3])}.`;
|
|
33555
33720
|
}
|
|
33556
|
-
const table2 = code === "LOANWORD_ERROR" ? LOANWORD_FIXES : code === "DISCRIMINATORY_TERM" ? DISCRIM_FIXES : null;
|
|
33721
|
+
const table2 = code === "LOANWORD_ERROR" ? LOANWORD_FIXES : code === "DISCRIMINATORY_TERM" ? DISCRIM_FIXES : code === "PLAIN_LANGUAGE" ? PLAIN_FIXES : code === "SPELLING_ERROR" ? SPELLING_FIXES : null;
|
|
33557
33722
|
if (!table2) return void 0;
|
|
33558
33723
|
const hit = table2.find(([w]) => w === match.trim());
|
|
33559
33724
|
return hit ? `${hit[0]} \u2192 ${hit[1]}` : void 0;
|
|
@@ -33799,6 +33964,60 @@ function muncheLintWarnings(text, limit = 10) {
|
|
|
33799
33964
|
// src/roundtrip/patcher.ts
|
|
33800
33965
|
import JSZip12 from "jszip";
|
|
33801
33966
|
|
|
33967
|
+
// src/roundtrip/integrity.ts
|
|
33968
|
+
import { DOMParser as DOMParser6 } from "@xmldom/xmldom";
|
|
33969
|
+
import { inflateRawSync as inflateRawSync6 } from "zlib";
|
|
33970
|
+
var LINESEG_RE = /<(\w+:)?linesegarray\b[^>]*?(?:\/>|>[\s\S]*?<\/\1linesegarray>)/g;
|
|
33971
|
+
var TAG_RE2 = /<[^>]+>/g;
|
|
33972
|
+
var sameBytes = (a, b) => Buffer.compare(a, b) === 0;
|
|
33973
|
+
function xmlError(xml) {
|
|
33974
|
+
let error = null;
|
|
33975
|
+
try {
|
|
33976
|
+
new DOMParser6({ onError: (level, msg2) => {
|
|
33977
|
+
if (level !== "warning" && !error) error = String(msg2);
|
|
33978
|
+
} }).parseFromString(xml, "text/xml");
|
|
33979
|
+
} catch (e) {
|
|
33980
|
+
error ??= e instanceof Error ? e.message : String(e);
|
|
33981
|
+
}
|
|
33982
|
+
return error;
|
|
33983
|
+
}
|
|
33984
|
+
var textOnly = (xml, s) => !s.replacement.includes("<") && !xml.slice(s.start, s.end).includes("<");
|
|
33985
|
+
function checkPatchIntegrity(original, patched, replacements, sections) {
|
|
33986
|
+
const issues = [];
|
|
33987
|
+
const a = readZipEntries(original), b = readZipEntries(patched);
|
|
33988
|
+
const names = (m) => [...m.keys()].join("\n");
|
|
33989
|
+
if (names(a) !== names(b)) issues.push("ZIP \uC5D4\uD2B8\uB9AC \uBAA9\uB85D\xB7\uC21C\uC11C\uAC00 \uC6D0\uBCF8\uACFC \uB2E4\uB984");
|
|
33990
|
+
for (const [name, entry] of a) {
|
|
33991
|
+
if (replacements.has(name)) continue;
|
|
33992
|
+
const out = b.get(name);
|
|
33993
|
+
if (!out || out.method !== entry.method || !sameBytes(out.compData, entry.compData)) issues.push(`\uAD50\uCCB4\uD558\uC9C0 \uC54A\uC740 \uC5D4\uD2B8\uB9AC\uC758 \uBC14\uC774\uD2B8\uAC00 \uBC14\uB01C: ${name}`);
|
|
33994
|
+
}
|
|
33995
|
+
const decoder = new TextDecoder();
|
|
33996
|
+
const originalText = (name) => {
|
|
33997
|
+
const e = a.get(name);
|
|
33998
|
+
if (!e) return null;
|
|
33999
|
+
try {
|
|
34000
|
+
return decoder.decode(e.method === 8 ? inflateRawSync6(e.compData) : e.compData);
|
|
34001
|
+
} catch {
|
|
34002
|
+
return null;
|
|
34003
|
+
}
|
|
34004
|
+
};
|
|
34005
|
+
for (const [name, data] of replacements) {
|
|
34006
|
+
if (!/\.xml$/i.test(name)) continue;
|
|
34007
|
+
const err = xmlError(decoder.decode(data));
|
|
34008
|
+
const before2 = originalText(name);
|
|
34009
|
+
if (err && before2 !== null && !xmlError(before2)) issues.push(`\uAD50\uCCB4\uD55C XML \uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC74C: ${name} \u2014 ${err.split("\n")[0]}`);
|
|
34010
|
+
}
|
|
34011
|
+
for (const { name, xml, splices } of sections) {
|
|
34012
|
+
if (!splices.every((s) => textOnly(xml, s))) continue;
|
|
34013
|
+
const data = replacements.get(name);
|
|
34014
|
+
if (!data) continue;
|
|
34015
|
+
const tags = (x) => (x.replace(LINESEG_RE, "").match(TAG_RE2) ?? []).join("");
|
|
34016
|
+
if (tags(xml) !== tags(decoder.decode(data))) issues.push(`\uAE00\uB9CC \uACE0\uCE5C \uC139\uC158\uC758 \uD0DC\uADF8\xB7\uC18D\uC131\uC774 \uBC14\uB01C: ${name}`);
|
|
34017
|
+
}
|
|
34018
|
+
return issues;
|
|
34019
|
+
}
|
|
34020
|
+
|
|
33802
34021
|
// src/roundtrip/table-rows.ts
|
|
33803
34022
|
var ROW_OBJECT_RE = /<(?:[A-Za-z0-9_]+:)?(?:tbl|pic|equation|ole|container|shape|drawingObject|drawText|video|chart|fieldBegin|fieldEnd|ctrl)\b/;
|
|
33804
34023
|
var TAG_AT_RE = /<[A-Za-z0-9_:]+(?:"[^"]*"|'[^']*'|[^>"'])*>/y;
|
|
@@ -35758,9 +35977,9 @@ async function patchHwpx(original, editedMarkdown, options) {
|
|
|
35758
35977
|
for (const [oi, ei] of pairs) {
|
|
35759
35978
|
if (oi !== null && ei !== null) {
|
|
35760
35979
|
const orig = origUnits[oi];
|
|
35761
|
-
const
|
|
35762
|
-
if (orig.raw ===
|
|
35763
|
-
applied += handleModifiedUnit(orig,
|
|
35980
|
+
const edited2 = editedUnits[ei];
|
|
35981
|
+
if (orig.raw === edited2.raw) continue;
|
|
35982
|
+
applied += handleModifiedUnit(orig, edited2, {
|
|
35764
35983
|
origBlocks,
|
|
35765
35984
|
paraMap,
|
|
35766
35985
|
scans,
|
|
@@ -35777,10 +35996,12 @@ async function patchHwpx(original, editedMarkdown, options) {
|
|
|
35777
35996
|
}
|
|
35778
35997
|
}
|
|
35779
35998
|
const replacements = /* @__PURE__ */ new Map();
|
|
35999
|
+
const edited = [];
|
|
35780
36000
|
const encoder = new TextEncoder();
|
|
35781
36001
|
try {
|
|
35782
36002
|
for (let i = 0; i < scans.length; i++) {
|
|
35783
36003
|
if (sectionSplices[i].length === 0) continue;
|
|
36004
|
+
edited.push({ name: sectionPaths[i], xml: scans[i].xml, splices: [...sectionSplices[i]] });
|
|
35784
36005
|
const claimed = sectionSplices[i].filter((s) => s.end > s.start);
|
|
35785
36006
|
sectionSplices[i].push(...allLinesegRemovalSplices(scans[i].xml).filter((ls) => !claimed.some((c) => ls.start >= c.start && ls.end <= c.end)));
|
|
35786
36007
|
const newXml = applySplices(scans[i].xml, sectionSplices[i]);
|
|
@@ -35805,6 +36026,8 @@ async function patchHwpx(original, editedMarkdown, options) {
|
|
|
35805
36026
|
}
|
|
35806
36027
|
let verification;
|
|
35807
36028
|
if (options?.verify !== false) {
|
|
36029
|
+
const issues = replacements.size > 0 ? checkPatchIntegrity(original, data, replacements, edited) : [];
|
|
36030
|
+
if (issues.length) return { success: false, applied, skipped, error: `\uD328\uCE58\uBCF8 \uBB34\uACB0\uC131 \uAC80\uC0AC \uC2E4\uD328 \u2014 \uD328\uCE58 \uC911\uB2E8: ${issues.join("; ")}` };
|
|
35808
36031
|
try {
|
|
35809
36032
|
const reparsed = await parseHwpxDocument(u8ToArrayBuffer(data), { layoutTables: "keep" });
|
|
35810
36033
|
verification = diffUnitLists(splitMarkdownUnits(reparsed.markdown), editedUnits);
|
|
@@ -36174,7 +36397,7 @@ async function resolveHeaderEntryName(zip) {
|
|
|
36174
36397
|
|
|
36175
36398
|
// src/validate.ts
|
|
36176
36399
|
import JSZip13 from "jszip";
|
|
36177
|
-
import { DOMParser as
|
|
36400
|
+
import { DOMParser as DOMParser7 } from "@xmldom/xmldom";
|
|
36178
36401
|
var REQUIRED_FILES = [
|
|
36179
36402
|
"mimetype",
|
|
36180
36403
|
"META-INF/container.xml",
|
|
@@ -36220,7 +36443,7 @@ async function validateHwpx(buffer) {
|
|
|
36220
36443
|
const text = await zip.files[name].async("string");
|
|
36221
36444
|
let firstError = null;
|
|
36222
36445
|
try {
|
|
36223
|
-
new
|
|
36446
|
+
new DOMParser7({
|
|
36224
36447
|
onError(level, msg2) {
|
|
36225
36448
|
if (level !== "warning" && firstError === null) firstError = String(msg2);
|
|
36226
36449
|
}
|
|
@@ -41703,7 +41926,7 @@ function scriptsOff(r, off) {
|
|
|
41703
41926
|
}
|
|
41704
41927
|
async function parseImage(buffer, options) {
|
|
41705
41928
|
try {
|
|
41706
|
-
const { parseImageDocument } = await import("./image-ocr-
|
|
41929
|
+
const { parseImageDocument } = await import("./image-ocr-YYTFOUSY.js");
|
|
41707
41930
|
const { blocks, warnings } = await parseImageDocument(buffer, options);
|
|
41708
41931
|
stripScriptTags({ blocks });
|
|
41709
41932
|
return {
|
|
@@ -41763,7 +41986,7 @@ async function parseHwp(buffer, options) {
|
|
|
41763
41986
|
async function parsePdf(buffer, options) {
|
|
41764
41987
|
let parsePdfDocument;
|
|
41765
41988
|
try {
|
|
41766
|
-
const mod = await import("./parser-
|
|
41989
|
+
const mod = await import("./parser-LY67CB7A.js");
|
|
41767
41990
|
parsePdfDocument = mod.parsePdfDocument;
|
|
41768
41991
|
} catch {
|
|
41769
41992
|
return {
|