kordoc 2.4.1 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/dist/{chunk-VYFIAYCW.js → chunk-KO7DKAXW.js} +2 -2
- package/dist/{chunk-T65PPCNU.js → chunk-OCVWJSG7.js} +2 -2
- package/dist/{chunk-JFPF7B5L.js → chunk-QEZ4CUF7.js} +78 -8
- package/dist/chunk-QEZ4CUF7.js.map +1 -0
- package/dist/{chunk-IVC5CB2Q.cjs → chunk-TTSFPEDM.cjs} +2 -2
- package/dist/{chunk-IVC5CB2Q.cjs.map → chunk-TTSFPEDM.cjs.map} +1 -1
- package/dist/cli.js +3 -3
- package/dist/index.cjs +162 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +77 -7
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +3 -3
- package/dist/{parser-ZORW4RSC.cjs → parser-BOIVVDYI.cjs} +16 -15
- package/dist/{parser-ZORW4RSC.cjs.map → parser-BOIVVDYI.cjs.map} +1 -1
- package/dist/{parser-UHUCMAA7.js → parser-DA3CGOZF.js} +3 -2
- package/dist/parser-DA3CGOZF.js.map +1 -0
- package/dist/{parser-VXUBNDG4.js → parser-NZFDRZLS.js} +3 -2
- package/dist/parser-NZFDRZLS.js.map +1 -0
- package/dist/{watch-SSENKOE2.js → watch-HWN6Y6Q2.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-JFPF7B5L.js.map +0 -1
- package/dist/parser-UHUCMAA7.js.map +0 -1
- package/dist/parser-VXUBNDG4.js.map +0 -1
- /package/dist/{chunk-VYFIAYCW.js.map → chunk-KO7DKAXW.js.map} +0 -0
- /package/dist/{chunk-T65PPCNU.js.map → chunk-OCVWJSG7.js.map} +0 -0
- /package/dist/{watch-SSENKOE2.js.map → watch-HWN6Y6Q2.js.map} +0 -0
package/README.md
CHANGED
|
@@ -31,6 +31,12 @@ Windows 도 자동으로 `cmd /c npx` 래핑. 수동 JSON 편집 불필요. 재
|
|
|
31
31
|
|
|
32
32
|
> **CLI 로만 쓸 거면** 설치 없이 `npx kordoc <파일>` 바로 사용. 아래 [CLI](#cli) 섹션 참고.
|
|
33
33
|
|
|
34
|
+
> **`MODULE_NOT_FOUND` / `Cannot find module ...\dist\cli.js` 가 뜨면**: 과거에 깨진 글로벌 설치가 남아있는 상태입니다. 아래로 해결:
|
|
35
|
+
> ```powershell
|
|
36
|
+
> npm uninstall -g kordoc
|
|
37
|
+
> npx -y kordoc@latest setup
|
|
38
|
+
> ```
|
|
39
|
+
|
|
34
40
|
---
|
|
35
41
|
|
|
36
42
|
## 💡 kordoc으로 무엇을 할 수 있나요?
|
|
@@ -46,10 +52,18 @@ Windows 도 자동으로 `cmd /c npx` 래핑. 수동 JSON 편집 불필요. 재
|
|
|
46
52
|
|
|
47
53
|
---
|
|
48
54
|
|
|
49
|
-
## v2.
|
|
55
|
+
## v2.5.0 변경사항
|
|
56
|
+
|
|
57
|
+
- **🏛️ macOS 한컴오피스 호환 HWPX 생성** (#4) — `markdownToHwpx()` 가 만든 HWPX 가 macOS 한컴에서 "파일이 깨졌다"며 거부되던 문제 해결. 테이블 XML 을 최소 스켈레톤에서 완전 스펙 형태로 재작성 — `<hp:tbl>` 필수 속성 10종 + `<hp:sz>`/`<hp:pos>`/`<hp:outMargin>`/`<hp:inMargin>`, `<hp:tc>` 안에 `<hp:subList>` 래퍼 + `<hp:cellAddr>`/`<hp:cellSpan>`/`<hp:cellSz>`/`<hp:cellMargin>`, paragraph 래핑. `Preview/PrvText.txt` 추가 + `borderFill` id=1(SOLID 0.12mm) 추가.
|
|
58
|
+
- **🔓 HWP 5.x 배포용 문서 COM fallback** (#25) — `.hwp` 바이너리에서 "이 문서는 상위 버전의 배포용 문서입니다..." 경고 플레이스홀더만 나오는 케이스에서, Windows + 한컴오피스 환경이면 자동으로 `HWPFrame.HwpObject` COM API 로 재시도. v2.4.0 의 HWPX DRM fallback 인프라를 `.hwp` 에도 확장.
|
|
59
|
+
|
|
60
|
+
<details>
|
|
61
|
+
<summary>v2.4.0 변경사항</summary>
|
|
50
62
|
|
|
51
63
|
- **🔓 HWPX DRM 배포용 문서 자동 추출** — 공공기관 배포용 DRM이 걸린 HWPX 파일을 한컴 오피스 COM API로 자동 텍스트 추출. `manifest.xml`에서 암호화 감지 → `HWPFrame.HwpObject`의 `GetPageText`로 페이지별 추출 → Markdown 변환. Windows + 한컴 오피스 설치 환경에서 별도 설정 없이 동작.
|
|
52
64
|
|
|
65
|
+
</details>
|
|
66
|
+
|
|
53
67
|
<details>
|
|
54
68
|
<summary>v2.3.0 변경사항</summary>
|
|
55
69
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/utils.ts
|
|
4
|
-
var VERSION = true ? "2.
|
|
4
|
+
var VERSION = true ? "2.5.1" : "0.0.0-dev";
|
|
5
5
|
function toArrayBuffer(buf) {
|
|
6
6
|
if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
|
|
7
7
|
return buf.buffer;
|
|
@@ -454,4 +454,4 @@ export {
|
|
|
454
454
|
HEADING_RATIO_H2,
|
|
455
455
|
HEADING_RATIO_H3
|
|
456
456
|
};
|
|
457
|
-
//# sourceMappingURL=chunk-
|
|
457
|
+
//# sourceMappingURL=chunk-KO7DKAXW.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/utils.ts
|
|
2
|
-
var VERSION = true ? "2.
|
|
2
|
+
var VERSION = true ? "2.5.1" : "0.0.0-dev";
|
|
3
3
|
function toArrayBuffer(buf) {
|
|
4
4
|
if (buf.byteOffset === 0 && buf.byteLength === buf.buffer.byteLength) {
|
|
5
5
|
return buf.buffer;
|
|
@@ -447,4 +447,4 @@ export {
|
|
|
447
447
|
HEADING_RATIO_H2,
|
|
448
448
|
HEADING_RATIO_H3
|
|
449
449
|
};
|
|
450
|
-
//# sourceMappingURL=chunk-
|
|
450
|
+
//# sourceMappingURL=chunk-OCVWJSG7.js.map
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
sanitizeHref,
|
|
21
21
|
stripDtd,
|
|
22
22
|
toArrayBuffer
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-KO7DKAXW.js";
|
|
24
24
|
import {
|
|
25
25
|
parsePageRange
|
|
26
26
|
} from "./chunk-MOL7MDBG.js";
|
|
@@ -3342,8 +3342,21 @@ async function markdownToHwpx(markdown) {
|
|
|
3342
3342
|
zip.file("Contents/content.hpf", generateManifest());
|
|
3343
3343
|
zip.file("Contents/header.xml", generateHeaderXml());
|
|
3344
3344
|
zip.file("Contents/section0.xml", sectionXml);
|
|
3345
|
+
zip.file("Preview/PrvText.txt", buildPrvText(blocks));
|
|
3345
3346
|
return await zip.generateAsync({ type: "arraybuffer" });
|
|
3346
3347
|
}
|
|
3348
|
+
function buildPrvText(blocks) {
|
|
3349
|
+
const lines = [];
|
|
3350
|
+
let bytes = 0;
|
|
3351
|
+
for (const b of blocks) {
|
|
3352
|
+
const text = b.text || (b.rows ? b.rows.map((r) => r.join(" ")).join("\n") : "");
|
|
3353
|
+
if (!text) continue;
|
|
3354
|
+
lines.push(text);
|
|
3355
|
+
bytes += text.length * 3;
|
|
3356
|
+
if (bytes > 1024) break;
|
|
3357
|
+
}
|
|
3358
|
+
return lines.join("\n").slice(0, 1024);
|
|
3359
|
+
}
|
|
3347
3360
|
function parseMarkdownToBlocks(md) {
|
|
3348
3361
|
const lines = md.split("\n");
|
|
3349
3362
|
const blocks = [];
|
|
@@ -3578,7 +3591,7 @@ function generateHeaderXml() {
|
|
|
3578
3591
|
</hh:font>
|
|
3579
3592
|
</hh:fontface>
|
|
3580
3593
|
</hh:fontfaces>
|
|
3581
|
-
<hh:borderFills itemCnt="
|
|
3594
|
+
<hh:borderFills itemCnt="2">
|
|
3582
3595
|
<hh:borderFill id="0" threeD="0" shadow="0" centerLine="0" breakCellSeparateLine="0">
|
|
3583
3596
|
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
3584
3597
|
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
@@ -3589,6 +3602,16 @@ function generateHeaderXml() {
|
|
|
3589
3602
|
<hh:diagonal type="NONE" width="0.1mm" color="#000000"/>
|
|
3590
3603
|
<hh:fillInfo/>
|
|
3591
3604
|
</hh:borderFill>
|
|
3605
|
+
<hh:borderFill id="1" threeD="0" shadow="0" centerLine="0" breakCellSeparateLine="0">
|
|
3606
|
+
<hh:slash type="NONE" Crooked="0" isCounter="0"/>
|
|
3607
|
+
<hh:backSlash type="NONE" Crooked="0" isCounter="0"/>
|
|
3608
|
+
<hh:leftBorder type="SOLID" width="0.12mm" color="#000000"/>
|
|
3609
|
+
<hh:rightBorder type="SOLID" width="0.12mm" color="#000000"/>
|
|
3610
|
+
<hh:topBorder type="SOLID" width="0.12mm" color="#000000"/>
|
|
3611
|
+
<hh:bottomBorder type="SOLID" width="0.12mm" color="#000000"/>
|
|
3612
|
+
<hh:diagonal type="NONE" width="0.1mm" color="#000000"/>
|
|
3613
|
+
<hh:fillInfo/>
|
|
3614
|
+
</hh:borderFill>
|
|
3592
3615
|
</hh:borderFills>
|
|
3593
3616
|
<hh:charProperties itemCnt="9">
|
|
3594
3617
|
${charPr(0, 1e3, false, false)}
|
|
@@ -3624,15 +3647,31 @@ ${paraPr(7, { align: "LEFT", lineSpacing: 160, indent: 600 })}
|
|
|
3624
3647
|
function generateSecPr() {
|
|
3625
3648
|
return `<hp:secPr textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" outlineShapeIDRef="0" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0"><hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/><hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/><hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/><hp:pagePr landscape="WIDELY" width="59528" height="84188" gutterType="LEFT_ONLY"><hp:margin header="2835" footer="2835" gutter="0" left="5670" right="4252" top="8504" bottom="4252"/></hp:pagePr><hp:footNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="283" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="EACH_COLUMN" beneathText="0"/></hp:footNotePr><hp:endNotePr><hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/><hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/><hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/><hp:numbering type="CONTINUOUS" newNum="1"/><hp:placement place="END_OF_DOCUMENT" beneathText="0"/></hp:endNotePr></hp:secPr>`;
|
|
3626
3649
|
}
|
|
3650
|
+
var TABLE_ID_BASE = 1e3;
|
|
3651
|
+
var tableIdCounter = TABLE_ID_BASE;
|
|
3652
|
+
function nextTableId() {
|
|
3653
|
+
return ++tableIdCounter;
|
|
3654
|
+
}
|
|
3627
3655
|
function generateTable(rows) {
|
|
3628
|
-
const
|
|
3629
|
-
|
|
3656
|
+
const rowCnt = rows.length;
|
|
3657
|
+
const colCnt = Math.max(...rows.map((r) => r.length), 1);
|
|
3658
|
+
const cellW = Math.floor(44e3 / colCnt);
|
|
3659
|
+
const cellH = 1500;
|
|
3660
|
+
const tblW = cellW * colCnt;
|
|
3661
|
+
const tblH = cellH * rowCnt;
|
|
3662
|
+
const tblId = nextTableId();
|
|
3663
|
+
const trElements = rows.map((row, rowIdx) => {
|
|
3664
|
+
const cells = row.length < colCnt ? [...row, ...Array(colCnt - row.length).fill("")] : row;
|
|
3665
|
+
const tdElements = cells.map((cell, colIdx) => {
|
|
3630
3666
|
const runs = generateRuns(cell);
|
|
3631
|
-
|
|
3667
|
+
const p = `<hp:p paraPrIDRef="0" styleIDRef="0">${runs}</hp:p>`;
|
|
3668
|
+
return `<hp:tc name="" header="${rowIdx === 0 ? 1 : 0}" hasMargin="0" protect="0" editable="1" dirty="0" borderFillIDRef="1"><hp:subList id="" textDirection="HORIZONTAL" lineWrap="BREAK" vertAlign="TOP" linkListIDRef="0" linkListNextIDRef="0" textWidth="0" textHeight="0" hasTextRef="0" hasNumRef="0">${p}</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>`;
|
|
3632
3669
|
}).join("");
|
|
3633
3670
|
return `<hp:tr>${tdElements}</hp:tr>`;
|
|
3634
3671
|
}).join("");
|
|
3635
|
-
|
|
3672
|
+
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;
|
|
3673
|
+
const tbl = `<hp:tbl id="${tblId}" zOrder="0" numberingType="TABLE" pageBreak="CELL" repeatHeader="0" rowCnt="${rowCnt}" colCnt="${colCnt}" cellSpacing="0" borderFillIDRef="1" noShading="0">${tblInner}</hp:tbl>`;
|
|
3674
|
+
return `<hp:p paraPrIDRef="0" styleIDRef="0"><hp:run charPrIDRef="0">${tbl}</hp:run></hp:p>`;
|
|
3636
3675
|
}
|
|
3637
3676
|
function blocksToSectionXml(blocks) {
|
|
3638
3677
|
const paraXmls = [];
|
|
@@ -3699,6 +3738,20 @@ function blocksToSectionXml(blocks) {
|
|
|
3699
3738
|
// src/index.ts
|
|
3700
3739
|
import { readFile } from "fs/promises";
|
|
3701
3740
|
|
|
3741
|
+
// src/hwp5/sentinel.ts
|
|
3742
|
+
var SENTINEL_PATTERNS = [
|
|
3743
|
+
/상위\s*버전의\s*배포용\s*문서/,
|
|
3744
|
+
/최신\s*버전의\s*한글.*뷰어/,
|
|
3745
|
+
/문서를\s*읽으려면/
|
|
3746
|
+
];
|
|
3747
|
+
function isDistributionSentinel(markdown) {
|
|
3748
|
+
if (!markdown) return false;
|
|
3749
|
+
const hit = SENTINEL_PATTERNS.some((p) => p.test(markdown));
|
|
3750
|
+
if (!hit) return false;
|
|
3751
|
+
const stripped = markdown.split(/\r?\n/).filter((line) => !SENTINEL_PATTERNS.some((p) => p.test(line))).join("").replace(/\s+/g, "");
|
|
3752
|
+
return stripped.length < 120;
|
|
3753
|
+
}
|
|
3754
|
+
|
|
3702
3755
|
// src/xlsx/parser.ts
|
|
3703
3756
|
import JSZip4 from "jszip";
|
|
3704
3757
|
import { DOMParser as DOMParser3 } from "@xmldom/xmldom";
|
|
@@ -4711,6 +4764,23 @@ async function parseHwpx(buffer, options) {
|
|
|
4711
4764
|
async function parseHwp(buffer, options) {
|
|
4712
4765
|
try {
|
|
4713
4766
|
const { markdown, blocks, metadata, outline, warnings, images } = parseHwp5Document(Buffer.from(buffer), options);
|
|
4767
|
+
if (isDistributionSentinel(markdown) && isComFallbackAvailable() && options?.filePath) {
|
|
4768
|
+
try {
|
|
4769
|
+
const { pages, pageCount, warnings: comWarns } = extractTextViaCom(options.filePath);
|
|
4770
|
+
if (pages.some((p) => p && p.trim().length > 0)) {
|
|
4771
|
+
const com = comResultToParseResult(pages, pageCount, comWarns);
|
|
4772
|
+
return {
|
|
4773
|
+
success: true,
|
|
4774
|
+
fileType: "hwp",
|
|
4775
|
+
markdown: com.markdown,
|
|
4776
|
+
blocks: com.blocks,
|
|
4777
|
+
metadata: com.metadata,
|
|
4778
|
+
warnings: com.warnings
|
|
4779
|
+
};
|
|
4780
|
+
}
|
|
4781
|
+
} catch {
|
|
4782
|
+
}
|
|
4783
|
+
}
|
|
4714
4784
|
return { success: true, fileType: "hwp", markdown, blocks, metadata, outline, warnings, images: images?.length ? images : void 0 };
|
|
4715
4785
|
} catch (err) {
|
|
4716
4786
|
return { success: false, fileType: "hwp", error: err instanceof Error ? err.message : "HWP \uD30C\uC2F1 \uC2E4\uD328", code: classifyError(err) };
|
|
@@ -4719,7 +4789,7 @@ async function parseHwp(buffer, options) {
|
|
|
4719
4789
|
async function parsePdf(buffer, options) {
|
|
4720
4790
|
let parsePdfDocument;
|
|
4721
4791
|
try {
|
|
4722
|
-
const mod = await import("./parser-
|
|
4792
|
+
const mod = await import("./parser-DA3CGOZF.js");
|
|
4723
4793
|
parsePdfDocument = mod.parsePdfDocument;
|
|
4724
4794
|
} catch {
|
|
4725
4795
|
return {
|
|
@@ -4949,4 +5019,4 @@ export {
|
|
|
4949
5019
|
compare,
|
|
4950
5020
|
parse
|
|
4951
5021
|
};
|
|
4952
|
-
//# sourceMappingURL=chunk-
|
|
5022
|
+
//# sourceMappingURL=chunk-QEZ4CUF7.js.map
|