hwpkit-dev 0.0.2 → 0.0.3
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/ .npmignore +4 -2
- package/README.md +39 -2
- package/dist/index.d.mts +41 -14
- package/dist/index.d.ts +41 -14
- package/dist/index.js +3553 -1159
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3553 -1159
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/playground/index.html +346 -0
- package/playground/main.ts +302 -0
- package/playground/vite.config.ts +16 -0
- package/src/contract/decoder.ts +1 -0
- package/src/contract/encoder.ts +6 -1
- package/src/core/BaseDecoder.ts +118 -0
- package/src/core/BaseEncoder.ts +146 -0
- package/src/decoders/docx/DocxDecoder.ts +743 -151
- package/src/decoders/html/HtmlDecoder.ts +366 -0
- package/src/decoders/hwp/HwpScanner.ts +325 -157
- package/src/decoders/hwpx/HwpxDecoder.ts +785 -297
- package/src/decoders/md/MdDecoder.ts +4 -4
- package/src/encoders/docx/DocxEncoder.ts +504 -240
- package/src/encoders/html/HtmlEncoder.ts +17 -19
- package/src/encoders/hwp/HwpEncoder.ts +1466 -859
- package/src/encoders/hwpx/HwpxEncoder.ts +1477 -469
- package/src/encoders/hwpx/constants.ts +148 -0
- package/src/encoders/hwpx/utils.ts +198 -0
- package/src/encoders/md/MdEncoder.ts +20 -15
- package/src/model/builders.ts +4 -4
- package/src/model/doc-props.ts +19 -5
- package/src/model/doc-tree.ts +12 -4
- package/src/pipeline/Pipeline.ts +7 -3
- package/src/pipeline/registry.ts +13 -2
- package/src/safety/StyleBridge.ts +51 -6
- package/src/toolkit/ArchiveKit.ts +56 -0
- package/src/toolkit/StyleMapper.ts +221 -0
- package/src/toolkit/UnitConverter.ts +138 -0
- package/src/toolkit/XmlKit.ts +0 -5
- package/test-styling.ts +210 -0
- package/hwp-analyze.ts +0 -90
- package/inspect-doc.ts +0 -57
- package/output_test.hwp +0 -0
- package/test-docx-to-hwp.ts +0 -45
package/test-styling.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HWP 인코더 스타일 기능 테스트
|
|
3
|
+
*
|
|
4
|
+
* 테스트 항목:
|
|
5
|
+
* 1. 표 위치 배치 (align: left/center/right)
|
|
6
|
+
* 2. 표 선 스타일 (각 변별 선 종류, 굵기, 색상)
|
|
7
|
+
* 3. 텍스트 스타일 (글꼴, bold, italic, underline, 글색, 형광펜)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { buildRoot, buildSheet, buildGrid, buildRow, buildCell, buildPara, buildSpan } from './src/model/builders';
|
|
11
|
+
import { HwpEncoder } from './src/encoders/hwp/HwpEncoder';
|
|
12
|
+
import { HwpxEncoder } from './src/encoders/hwpx/HwpxEncoder';
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
|
|
15
|
+
// 표 정렬 및 선 스타일 테스트 문서
|
|
16
|
+
const doc = buildRoot(
|
|
17
|
+
{ title: 'HWP 인코더 스타일 테스트 문서' },
|
|
18
|
+
[
|
|
19
|
+
buildSheet([
|
|
20
|
+
// 제목
|
|
21
|
+
buildPara([
|
|
22
|
+
buildSpan('HWP 인코더 스타일 기능 테스트', { b: true, pt: 18 }),
|
|
23
|
+
]),
|
|
24
|
+
|
|
25
|
+
// 1. 표 정렬 테스트 - 왼쪽 정렬
|
|
26
|
+
buildPara([buildSpan('1. 표 정렬: 왼쪽 정렬', { b: true })]),
|
|
27
|
+
buildGrid(
|
|
28
|
+
[
|
|
29
|
+
buildRow([
|
|
30
|
+
buildCell([buildPara([buildSpan('왼쪽 정렬 표', { b: true })])]),
|
|
31
|
+
buildCell([buildPara([buildSpan('데이터', {})])]),
|
|
32
|
+
]),
|
|
33
|
+
buildRow([
|
|
34
|
+
buildCell([buildPara([buildSpan('셀 1', {})])]),
|
|
35
|
+
buildCell([buildPara([buildSpan('셀 2', {})])]),
|
|
36
|
+
]),
|
|
37
|
+
],
|
|
38
|
+
{
|
|
39
|
+
align: 'left',
|
|
40
|
+
defaultStroke: { kind: 'solid', pt: 1, color: '000000' },
|
|
41
|
+
colWidths: [100, 100]
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
|
|
45
|
+
// 2. 표 정렬 테스트 - 중앙 정렬
|
|
46
|
+
buildPara([buildSpan('2. 표 정렬: 중앙 정렬', { b: true })]),
|
|
47
|
+
buildGrid(
|
|
48
|
+
[
|
|
49
|
+
buildRow([
|
|
50
|
+
buildCell([buildPara([buildSpan('중앙 정렬 표', { b: true })])]),
|
|
51
|
+
buildCell([buildPara([buildSpan('데이터', {})])]),
|
|
52
|
+
]),
|
|
53
|
+
buildRow([
|
|
54
|
+
buildCell([buildPara([buildSpan('셀 1', {})])]),
|
|
55
|
+
buildCell([buildPara([buildSpan('셀 2', {})])]),
|
|
56
|
+
]),
|
|
57
|
+
],
|
|
58
|
+
{
|
|
59
|
+
align: 'center',
|
|
60
|
+
defaultStroke: { kind: 'solid', pt: 1, color: '000000' },
|
|
61
|
+
colWidths: [100, 100]
|
|
62
|
+
}
|
|
63
|
+
),
|
|
64
|
+
|
|
65
|
+
// 3. 표 정렬 테스트 - 오른쪽 정렬
|
|
66
|
+
buildPara([buildSpan('3. 표 정렬: 오른쪽 정렬', { b: true })]),
|
|
67
|
+
buildGrid(
|
|
68
|
+
[
|
|
69
|
+
buildRow([
|
|
70
|
+
buildCell([buildPara([buildSpan('오른쪽 정렬 표', { b: true })])]),
|
|
71
|
+
buildCell([buildPara([buildSpan('데이터', {})])]),
|
|
72
|
+
]),
|
|
73
|
+
buildRow([
|
|
74
|
+
buildCell([buildPara([buildSpan('셀 1', {})])]),
|
|
75
|
+
buildCell([buildPara([buildSpan('셀 2', {})])]),
|
|
76
|
+
]),
|
|
77
|
+
],
|
|
78
|
+
{
|
|
79
|
+
align: 'right',
|
|
80
|
+
defaultStroke: { kind: 'solid', pt: 1, color: '000000' },
|
|
81
|
+
colWidths: [100, 100]
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
|
|
85
|
+
// 4. 표 선 스타일 테스트 - 각 변별 다른 스타일
|
|
86
|
+
buildPara([buildSpan('4. 표 선 스타일: 각 변별 다른 스타일', { b: true })]),
|
|
87
|
+
buildGrid(
|
|
88
|
+
[
|
|
89
|
+
buildRow([
|
|
90
|
+
buildCell([buildPara([buildSpan('상단', {})])], {
|
|
91
|
+
top: { kind: 'double', pt: 2, color: '0000FF' }
|
|
92
|
+
}),
|
|
93
|
+
buildCell([buildPara([buildSpan('하단', {})])], {
|
|
94
|
+
bot: { kind: 'dash', pt: 1.5, color: 'FF0000' }
|
|
95
|
+
}),
|
|
96
|
+
]),
|
|
97
|
+
buildRow([
|
|
98
|
+
buildCell([buildPara([buildSpan('좌측', {})])], {
|
|
99
|
+
left: { kind: 'dot', pt: 1, color: '00FF00' }
|
|
100
|
+
}),
|
|
101
|
+
buildCell([buildPara([buildSpan('우측', {})])], {
|
|
102
|
+
right: { kind: 'solid', pt: 3, color: 'FFFF00' }
|
|
103
|
+
}),
|
|
104
|
+
]),
|
|
105
|
+
],
|
|
106
|
+
{ defaultStroke: { kind: 'solid', pt: 0.5, color: '808080' } }
|
|
107
|
+
),
|
|
108
|
+
|
|
109
|
+
// 5. 텍스트 스타일 테스트 - 글꼴
|
|
110
|
+
buildPara([buildSpan('5. 글꼴 테스트', { b: true })]),
|
|
111
|
+
buildPara([
|
|
112
|
+
buildSpan('배탕체', { font: 'Batang' }),
|
|
113
|
+
buildSpan(' + ', {}),
|
|
114
|
+
buildSpan('맑은고딕', { font: 'Malgun Gothic' }),
|
|
115
|
+
buildSpan(' + ', {}),
|
|
116
|
+
buildSpan('굴림체', { font: 'GulimChe' }),
|
|
117
|
+
]),
|
|
118
|
+
|
|
119
|
+
// 6. 텍스트 스타일 테스트 - 글자 모양
|
|
120
|
+
buildPara([buildSpan('6. 글자 모양 테스트', { b: true })]),
|
|
121
|
+
buildPara([
|
|
122
|
+
buildSpan('볼드', { b: true }),
|
|
123
|
+
buildSpan(' + ', {}),
|
|
124
|
+
buildSpan('이탤릭', { i: true }),
|
|
125
|
+
buildSpan(' + ', {}),
|
|
126
|
+
buildSpan('밑줄', { u: true }),
|
|
127
|
+
buildSpan(' + ', {}),
|
|
128
|
+
buildSpan('취소선', { s: true }),
|
|
129
|
+
]),
|
|
130
|
+
|
|
131
|
+
// 7. 텍스트 스타일 테스트 - 글색
|
|
132
|
+
buildPara([buildSpan('7. 글색 테스트', { b: true })]),
|
|
133
|
+
buildPara([
|
|
134
|
+
buildSpan('빨강', { color: 'FF0000' }),
|
|
135
|
+
buildSpan(' + ', {}),
|
|
136
|
+
buildSpan('파랑', { color: '0000FF' }),
|
|
137
|
+
buildSpan(' + ', {}),
|
|
138
|
+
buildSpan('초록', { color: '008000' }),
|
|
139
|
+
]),
|
|
140
|
+
|
|
141
|
+
// 8. 텍스트 스타일 테스트 - 형광펜 (배경색)
|
|
142
|
+
buildPara([buildSpan('8. 형광펜 테스트', { b: true })]),
|
|
143
|
+
buildPara([
|
|
144
|
+
buildSpan('노란 형광펜', { bg: 'FFFF00' }),
|
|
145
|
+
buildSpan(' + ', {}),
|
|
146
|
+
buildSpan('초록 형광펜', { bg: 'CCFFCC' }),
|
|
147
|
+
buildSpan(' + ', {}),
|
|
148
|
+
buildSpan('파랑 형광펜', { bg: 'CCCCFF' }),
|
|
149
|
+
]),
|
|
150
|
+
|
|
151
|
+
// 9. 복합 스타일 테스트
|
|
152
|
+
buildPara([buildSpan('9. 복합 스타일 테스트', { b: true })]),
|
|
153
|
+
buildPara([
|
|
154
|
+
buildSpan('볼드 + 빨강 + 노란 형광펜', {
|
|
155
|
+
b: true,
|
|
156
|
+
color: 'FF0000',
|
|
157
|
+
bg: 'FFFF00'
|
|
158
|
+
}),
|
|
159
|
+
]),
|
|
160
|
+
buildPara([
|
|
161
|
+
buildSpan('이탤릭 + 파랑 + 굴림체', {
|
|
162
|
+
i: true,
|
|
163
|
+
color: '0000FF',
|
|
164
|
+
font: 'GulimChe'
|
|
165
|
+
}),
|
|
166
|
+
]),
|
|
167
|
+
]),
|
|
168
|
+
]
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
async function runTest() {
|
|
172
|
+
console.log('=== HWP 인코더 스타일 테스트 ===\n');
|
|
173
|
+
|
|
174
|
+
// HWP 인코딩
|
|
175
|
+
console.log('HWP 인코딩 중...');
|
|
176
|
+
const hwpEncoder = new HwpEncoder();
|
|
177
|
+
const hwpOutcome = await hwpEncoder.encode(doc);
|
|
178
|
+
if (hwpOutcome.ok) {
|
|
179
|
+
fs.writeFileSync('test-styling.hwp', hwpOutcome.data);
|
|
180
|
+
console.log('✓ HWP 파일 생성 완료: test-styling.hwp');
|
|
181
|
+
console.log(` 파일 크기: ${hwpOutcome.data.length} bytes\n`);
|
|
182
|
+
} else {
|
|
183
|
+
console.error('✗ HWP 인코딩 실패:', hwpOutcome.err);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// HWPX 인코딩
|
|
188
|
+
console.log('HWPX 인코딩 중...');
|
|
189
|
+
const hwpxEncoder = new HwpxEncoder();
|
|
190
|
+
const hwpxOutcome = await hwpxEncoder.encode(doc);
|
|
191
|
+
if (hwpxOutcome.ok) {
|
|
192
|
+
fs.writeFileSync('test-styling.hwpx', hwpxOutcome.data);
|
|
193
|
+
console.log('✓ HWPX 파일 생성 완료: test-styling.hwpx');
|
|
194
|
+
console.log(` 파일 크기: ${hwpxOutcome.data.length} bytes\n`);
|
|
195
|
+
} else {
|
|
196
|
+
console.error('✗ HWPX 인코딩 실패:', hwpxOutcome.err);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log('=== 테스트 완료 ===');
|
|
201
|
+
console.log('생성된 파일을 HWP 뷰어로 열어 다음을 확인하세요:');
|
|
202
|
+
console.log(' 1. 표 정렬: 왼쪽/중앙/오른쪽 정렬이 적용되는지');
|
|
203
|
+
console.log(' 2. 표 선 스타일: 각 변별 다른 선 종류/굵기/색상이 적용되는지');
|
|
204
|
+
console.log(' 3. 글꼴: 배탕체/맑은고딕/굴림체가 적용되는지');
|
|
205
|
+
console.log(' 4. 글자 모양: bold/italic/underline/strikethrough 가 적용되는지');
|
|
206
|
+
console.log(' 5. 글색: 빨강/파랑/초록이 적용되는지');
|
|
207
|
+
console.log(' 6. 형광펜: 노랑/초록/파랑 배경색이 적용되는지');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
runTest().catch(console.error);
|
package/hwp-analyze.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import pako from 'pako';
|
|
3
|
-
|
|
4
|
-
const hwp = fs.readFileSync('/mnt/92b2cb7e-8f06-4e4f-bfde-a14c87f2f96c/Github/hwpkit/data/sample/sample1_input.hwp');
|
|
5
|
-
const SS = 512;
|
|
6
|
-
const buf = hwp.buffer.slice(hwp.byteOffset, hwp.byteOffset + hwp.length);
|
|
7
|
-
|
|
8
|
-
function u32LE(b: Buffer, off: number) {
|
|
9
|
-
return b[off] | (b[off+1]<<8) | (b[off+2]<<16) | ((b[off+3]&0xFF)*0x1000000);
|
|
10
|
-
}
|
|
11
|
-
function i32LE(b: Buffer, off: number) {
|
|
12
|
-
const v = u32LE(b,off);
|
|
13
|
-
return v >= 0x80000000 ? v - 0x100000000 : v;
|
|
14
|
-
}
|
|
15
|
-
function u16LE(b: Buffer, off: number) { return b[off] | (b[off+1]<<8); }
|
|
16
|
-
|
|
17
|
-
const hdr = hwp.slice(0, 512);
|
|
18
|
-
const fatSec = u32LE(hdr as Buffer, 76);
|
|
19
|
-
const dirSec = u32LE(hdr as Buffer, 48);
|
|
20
|
-
|
|
21
|
-
function sectorSlice(sec: number) {
|
|
22
|
-
const off = (sec+1)*SS;
|
|
23
|
-
return hwp.slice(off, off+SS) as Buffer;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const fatSector = sectorSlice(fatSec);
|
|
27
|
-
function nextSec(sec: number) { return u32LE(fatSector as Buffer, (sec%128)*4); }
|
|
28
|
-
|
|
29
|
-
function readChain(startSec: number, maxB = 200000): Buffer {
|
|
30
|
-
const chunks: Buffer[] = [];
|
|
31
|
-
let sec = startSec, tot = 0;
|
|
32
|
-
while (sec < 0xFFFFFFFE && tot < maxB) {
|
|
33
|
-
chunks.push(sectorSlice(sec) as Buffer);
|
|
34
|
-
tot += SS;
|
|
35
|
-
sec = nextSec(sec);
|
|
36
|
-
}
|
|
37
|
-
return Buffer.concat(chunks);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const dirBuf = readChain(dirSec, 2048);
|
|
41
|
-
function readEntry(idx: number) {
|
|
42
|
-
const base = idx*128;
|
|
43
|
-
const nl = u16LE(dirBuf, base+64);
|
|
44
|
-
let name = '';
|
|
45
|
-
for (let i=0;i<(nl/2)-1&&i<32;i++) name += String.fromCharCode(u16LE(dirBuf, base+i*2));
|
|
46
|
-
return {
|
|
47
|
-
name, type: dirBuf[base+66],
|
|
48
|
-
child: i32LE(dirBuf, base+76), right: i32LE(dirBuf, base+72),
|
|
49
|
-
startSec: u32LE(dirBuf, base+116), size: u32LE(dirBuf, base+120)
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let docInfoE: any = null, sec0E: any = null;
|
|
54
|
-
function walk(idx: number, depth = 0) {
|
|
55
|
-
if (idx < 0 || idx > 100) return;
|
|
56
|
-
const e = readEntry(idx);
|
|
57
|
-
if (depth < 5) console.log(' '.repeat(depth*2) + idx + ': "' + e.name + '" type=' + e.type + ' sz=' + e.size);
|
|
58
|
-
if (e.name === 'DocInfo') docInfoE = e;
|
|
59
|
-
if (e.name === 'Section0') sec0E = e;
|
|
60
|
-
walk(e.child, depth+1);
|
|
61
|
-
walk(e.right, depth);
|
|
62
|
-
}
|
|
63
|
-
walk(0);
|
|
64
|
-
|
|
65
|
-
if (!docInfoE || !sec0E) { console.log('NOT FOUND'); process.exit(1); }
|
|
66
|
-
|
|
67
|
-
const diC = readChain(docInfoE.startSec, docInfoE.size+512);
|
|
68
|
-
const s0C = readChain(sec0E.startSec, sec0E.size+512);
|
|
69
|
-
const diRaw = Buffer.from(pako.inflate(diC.slice(0,docInfoE.size)));
|
|
70
|
-
const s0Raw = Buffer.from(pako.inflate(s0C.slice(0,sec0E.size)));
|
|
71
|
-
|
|
72
|
-
function parseRecs(buf: Buffer) {
|
|
73
|
-
const recs: any[] = []; let off = 0;
|
|
74
|
-
while (off+4 <= buf.length) {
|
|
75
|
-
const h = u32LE(buf,off), tag=h&0x3FF, lv=(h>>10)&0x3FF;
|
|
76
|
-
let sz=(h>>>20)&0xFFF, hSz=4;
|
|
77
|
-
if(sz===0xFFF){sz=u32LE(buf,off+4);hSz=8;}
|
|
78
|
-
if(off+hSz+sz>buf.length) break;
|
|
79
|
-
recs.push({tag,lv,sz,data:buf.slice(off+hSz,off+hSz+sz)});
|
|
80
|
-
off+=hSz+sz;
|
|
81
|
-
}
|
|
82
|
-
return recs;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const s0 = parseRecs(s0Raw);
|
|
86
|
-
console.log('\nSection0 records:', s0.length);
|
|
87
|
-
for (const r of s0.slice(0, 50)) {
|
|
88
|
-
const h = r.data.slice(0,Math.min(r.sz,50)).toString('hex');
|
|
89
|
-
console.log(`tag=${r.tag} lv=${r.lv} sz=${r.sz} [${h}]`);
|
|
90
|
-
}
|
package/inspect-doc.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { Pipeline, TreeWalker, countNodes } from './src/index';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
|
|
4
|
-
async function inspectDoc(filePath: string, description: string) {
|
|
5
|
-
console.log(`\n📄 ${description}`);
|
|
6
|
-
console.log(` File: ${filePath} (${fs.statSync(filePath).size} bytes)`);
|
|
7
|
-
|
|
8
|
-
const data = fs.readFileSync(filePath);
|
|
9
|
-
const pipeline = Pipeline.open(data);
|
|
10
|
-
const result = await pipeline.inspect();
|
|
11
|
-
|
|
12
|
-
if (!result.ok) {
|
|
13
|
-
console.error(` ❌ FAILED: ${result.error}`);
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const doc = result.data;
|
|
18
|
-
console.log(` Meta: title="${doc.meta.title}", author="${doc.meta.author}"`);
|
|
19
|
-
console.log(` Sheets: ${doc.kids.length}`);
|
|
20
|
-
|
|
21
|
-
const walker = new TreeWalker();
|
|
22
|
-
let paraCount = 0, spanCount = 0, txtCount = 0, imgCount = 0, gridCount = 0;
|
|
23
|
-
let totalChars = 0;
|
|
24
|
-
|
|
25
|
-
walker.walk(doc, (node) => {
|
|
26
|
-
switch (node.tag) {
|
|
27
|
-
case 'para': paraCount++; break;
|
|
28
|
-
case 'span': spanCount++; break;
|
|
29
|
-
case 'txt': txtCount++; totalChars += node.content.length; break;
|
|
30
|
-
case 'img': imgCount++; break;
|
|
31
|
-
case 'grid': gridCount++; break;
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
console.log(` Paragraphs: ${paraCount}`);
|
|
36
|
-
console.log(` Spans: ${spanCount}`);
|
|
37
|
-
console.log(` Text nodes: ${txtCount}, Total chars: ${totalChars}`);
|
|
38
|
-
console.log(` Images: ${imgCount}`);
|
|
39
|
-
console.log(` Tables (grids): ${gridCount}`);
|
|
40
|
-
|
|
41
|
-
if (result.warns.length > 0) {
|
|
42
|
-
console.log(` ⚠️ Warnings: ${result.warns.slice(0, 5).join(', ')}${result.warns.length > 5 ? '...' : ''}`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function main() {
|
|
47
|
-
// Input files
|
|
48
|
-
await inspectDoc('./data/sample/sample1_input.hwp', 'Sample1 Input (HWP)');
|
|
49
|
-
await inspectDoc('./data/sample/sample3_input.hwpx', 'Sample3 Input (HWPX)');
|
|
50
|
-
await inspectDoc('./data/sample/sample4_input.docx', 'Sample4 Input (DOCX)');
|
|
51
|
-
|
|
52
|
-
// Expected outputs
|
|
53
|
-
await inspectDoc('./data/sample/sample1_output.docx', 'Sample1 Expected Output (DOCX)');
|
|
54
|
-
await inspectDoc('./data/sample/sample1_output.hwpx', 'Sample1 Expected Output (HWPX)');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
main().catch(console.error);
|
package/output_test.hwp
DELETED
|
Binary file
|
package/test-docx-to-hwp.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { Pipeline } from './src/index';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
|
|
4
|
-
async function testDocxToHwp() {
|
|
5
|
-
const inputPath = './data/sample/sample4_input.docx';
|
|
6
|
-
console.log(`\n📄 Testing DOCX → HWP conversion from: ${inputPath}`);
|
|
7
|
-
const data = fs.readFileSync(inputPath);
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
const pipeline = Pipeline.open(data, 'docx');
|
|
11
|
-
|
|
12
|
-
console.log('Attempting to convert to HWP...');
|
|
13
|
-
const result = await pipeline.to('hwp');
|
|
14
|
-
|
|
15
|
-
if (result.ok) {
|
|
16
|
-
console.log(`✅ Success! HWP output: ${result.data.length} bytes`);
|
|
17
|
-
|
|
18
|
-
// Save to file for verification
|
|
19
|
-
fs.writeFileSync('./output_test.hwp', result.data);
|
|
20
|
-
console.log('Saved to: ./output_test.hwp');
|
|
21
|
-
|
|
22
|
-
// Verify by converting back to MD
|
|
23
|
-
console.log('\n--- Verifying by converting back to MD ---');
|
|
24
|
-
const verifyPipeline = Pipeline.open(result.data, 'hwp');
|
|
25
|
-
const mdResult = await verifyPipeline.to('md');
|
|
26
|
-
if (mdResult.ok) {
|
|
27
|
-
const mdText = new TextDecoder().decode(mdResult.data);
|
|
28
|
-
console.log(`Verification MD output: ${mdText.length} bytes`);
|
|
29
|
-
console.log('First 500 chars:', mdText.substring(0, 500));
|
|
30
|
-
} else {
|
|
31
|
-
console.log(`Verification failed: ${mdResult.error}`);
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
console.log(`❌ Failed: ${result.error}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return result.ok;
|
|
38
|
-
} catch (e: any) {
|
|
39
|
-
console.error(`❌ EXCEPTION: ${e.message}`);
|
|
40
|
-
console.error(e.stack);
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
testDocxToHwp().catch(console.error);
|