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
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { Encoder } from '../../contract/encoder';
|
|
2
1
|
import type { DocRoot, ParaNode, SpanNode, GridNode, ContentNode, ImgNode, LinkNode } from '../../model/doc-tree';
|
|
3
2
|
import type { Outcome } from '../../contract/result';
|
|
4
3
|
import { succeed, fail } from '../../contract/result';
|
|
5
4
|
import { TextKit } from '../../toolkit/TextKit';
|
|
6
5
|
import { registry } from '../../pipeline/registry';
|
|
6
|
+
import { BaseEncoder } from '../../core/BaseEncoder';
|
|
7
7
|
|
|
8
|
-
export class HtmlEncoder
|
|
9
|
-
|
|
8
|
+
export class HtmlEncoder extends BaseEncoder {
|
|
9
|
+
protected getFormat(): string { return 'html'; }
|
|
10
10
|
|
|
11
11
|
async encode(doc: DocRoot): Promise<Outcome<Uint8Array>> {
|
|
12
12
|
try {
|
|
@@ -15,8 +15,8 @@ export class HtmlEncoder implements Encoder {
|
|
|
15
15
|
|
|
16
16
|
for (const sheet of doc.kids) {
|
|
17
17
|
// Header/footer as comments
|
|
18
|
-
if (sheet.
|
|
19
|
-
const hText = sheet.
|
|
18
|
+
if (sheet.headers?.default && sheet.headers.default.length > 0) {
|
|
19
|
+
const hText = sheet.headers.default.map((p: ParaNode) => encodePara(p, warns)).join('');
|
|
20
20
|
bodyParts.push(`<div class="hwp-header">${hText}</div>`);
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -24,16 +24,16 @@ export class HtmlEncoder implements Encoder {
|
|
|
24
24
|
bodyParts.push(encodeContent(kid, warns));
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
if (sheet.
|
|
28
|
-
const fText = sheet.
|
|
27
|
+
if (sheet.footers?.default && sheet.footers.default.length > 0) {
|
|
28
|
+
const fText = sheet.footers.default.map((p: ParaNode) => encodePara(p, warns)).join('');
|
|
29
29
|
bodyParts.push(`<div class="hwp-footer">${fText}</div>`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const title =
|
|
33
|
+
const title = this.escapeXml(doc.meta?.title ?? '');
|
|
34
34
|
const html = `<!DOCTYPE html>\n<html lang="ko">\n<head>\n<meta charset="UTF-8">\n<meta name="viewport" content="width=device-width, initial-scale=1.0">\n<title>${title}</title>\n<style>\n${BASE_CSS}\n</style>\n</head>\n<body>\n<div class="hwp-doc">\n${bodyParts.join('\n')}\n</div>\n</body>\n</html>`;
|
|
35
35
|
|
|
36
|
-
return succeed(
|
|
36
|
+
return succeed(this.stringToBytes(html), warns);
|
|
37
37
|
} catch (e: any) {
|
|
38
38
|
return fail(`HTML encode error: ${e?.message ?? String(e)}`);
|
|
39
39
|
}
|
|
@@ -45,7 +45,7 @@ body { margin: 0; padding: 0; background: #f0f0f0; }
|
|
|
45
45
|
.hwp-doc { max-width: 800px; margin: 0 auto; background: #fff; padding: 40px 60px; box-shadow: 0 0 8px rgba(0,0,0,0.15); }
|
|
46
46
|
.hwp-header, .hwp-footer { color: #666; font-size: 0.9em; border-bottom: 1px solid #ddd; margin-bottom: 8px; padding-bottom: 4px; }
|
|
47
47
|
.hwp-footer { border-top: 1px solid #ddd; border-bottom: none; margin-top: 8px; padding-top: 4px; }
|
|
48
|
-
p { margin: 0; padding: 0; line-height: 1
|
|
48
|
+
p { margin: 0; padding: 0; line-height: 1; }
|
|
49
49
|
table { border-collapse: collapse; width: 100%; margin: 8px 0; }
|
|
50
50
|
td, th { border: 1px solid #ccc; padding: 4px 8px; vertical-align: top; }
|
|
51
51
|
img { max-width: 100%; height: auto; }
|
|
@@ -62,7 +62,7 @@ function encodePara(para: ParaNode, warns: string[]): string {
|
|
|
62
62
|
if (k.tag === 'link') {
|
|
63
63
|
const link = k as LinkNode;
|
|
64
64
|
const inner = link.kids.map(s => encodeSpan(s, warns)).join('');
|
|
65
|
-
return `<a href="${
|
|
65
|
+
return `<a href="${TextKit.escapeXml(link.href)}">${inner}</a>`;
|
|
66
66
|
}
|
|
67
67
|
return '';
|
|
68
68
|
}).join('');
|
|
@@ -100,7 +100,9 @@ function encodeSpan(span: SpanNode, warns: string[]): string {
|
|
|
100
100
|
|
|
101
101
|
for (const kid of span.kids) {
|
|
102
102
|
if (kid.tag === 'txt') {
|
|
103
|
-
|
|
103
|
+
// __EXT_N__ 또는 __EXT_N_W<w>_H<h>__ 자리표시자 제거
|
|
104
|
+
const content = kid.content.replace(/__EXT_\d+(?:_W\d+_H\d+)?__/g, '');
|
|
105
|
+
if (content) parts.push(TextKit.escapeXml(content));
|
|
104
106
|
} else if (kid.tag === 'br') {
|
|
105
107
|
parts.push('<br>');
|
|
106
108
|
} else if (kid.tag === 'pb') {
|
|
@@ -119,7 +121,7 @@ function encodeSpan(span: SpanNode, warns: string[]): string {
|
|
|
119
121
|
|
|
120
122
|
const p = span.props;
|
|
121
123
|
const css: string[] = [];
|
|
122
|
-
if (p.font) css.push(`font-family:${
|
|
124
|
+
if (p.font) css.push(`font-family:${TextKit.escapeXml(p.font)}`);
|
|
123
125
|
if (p.pt) css.push(`font-size:${p.pt}pt`);
|
|
124
126
|
if (p.color) css.push(`color:#${p.color}`);
|
|
125
127
|
if (p.bg) css.push(`background-color:#${p.bg}`);
|
|
@@ -140,7 +142,7 @@ function encodeSpan(span: SpanNode, warns: string[]): string {
|
|
|
140
142
|
function encodeImage(img: ImgNode): string {
|
|
141
143
|
const wStyle = img.w ? ` width="${Math.round(img.w / 72 * 96)}px"` : '';
|
|
142
144
|
const hStyle = img.h ? ` height="${Math.round(img.h / 72 * 96)}px"` : '';
|
|
143
|
-
const alt =
|
|
145
|
+
const alt = TextKit.escapeXml(img.alt ?? '');
|
|
144
146
|
return `<img src="data:${img.mime};base64,${img.b64}" alt="${alt}"${wStyle}${hStyle}>`;
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -188,7 +190,7 @@ function encodeGrid(grid: GridNode, warns: string[]): string {
|
|
|
188
190
|
else if (va === 'bot') styleAttrs.push('vertical-align:bottom');
|
|
189
191
|
const styleAttr = styleAttrs.length > 0 ? ` style="${styleAttrs.join(';')}"` : '';
|
|
190
192
|
|
|
191
|
-
const content = cell.kids.map(p => encodePara(p, warns)).join('');
|
|
193
|
+
const content = cell.kids.map(p => p.tag === 'para' ? encodePara(p, warns) : encodeGrid(p, warns)).join('');
|
|
192
194
|
cells += `<${tag}${cs}${rs}${styleAttr}>${content}</${tag}>`;
|
|
193
195
|
ci += cell.cs;
|
|
194
196
|
}
|
|
@@ -198,8 +200,4 @@ function encodeGrid(grid: GridNode, warns: string[]): string {
|
|
|
198
200
|
return `<table>\n<tbody>\n${rows}</tbody>\n</table>\n`;
|
|
199
201
|
}
|
|
200
202
|
|
|
201
|
-
function esc(s: string): string {
|
|
202
|
-
return TextKit.escapeXml(s);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
203
|
registry.registerEncoder(new HtmlEncoder());
|