hwp-convert 1.0.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/CHANGELOG.md +185 -0
- package/LICENSE +25 -0
- package/NOTICE +23 -0
- package/README.md +338 -0
- package/dist/browser/hwp-convert.browser.mjs +20677 -0
- package/dist/browser/hwp-convert.browser.mjs.map +7 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +267 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/lib/errors.d.ts +9 -0
- package/dist/lib/errors.js +18 -0
- package/dist/lib/hwp/binData.d.ts +15 -0
- package/dist/lib/hwp/binData.js +64 -0
- package/dist/lib/hwp/bodyText.d.ts +31 -0
- package/dist/lib/hwp/bodyText.js +208 -0
- package/dist/lib/hwp/byteReader.d.ts +40 -0
- package/dist/lib/hwp/byteReader.js +116 -0
- package/dist/lib/hwp/cfbReader.d.ts +44 -0
- package/dist/lib/hwp/cfbReader.js +134 -0
- package/dist/lib/hwp/control.d.ts +17 -0
- package/dist/lib/hwp/control.js +290 -0
- package/dist/lib/hwp/converter.d.ts +22 -0
- package/dist/lib/hwp/converter.js +41 -0
- package/dist/lib/hwp/docInfo.d.ts +26 -0
- package/dist/lib/hwp/docInfo.js +396 -0
- package/dist/lib/hwp/fileHeader.d.ts +42 -0
- package/dist/lib/hwp/fileHeader.js +66 -0
- package/dist/lib/hwp/htmlReader.d.ts +17 -0
- package/dist/lib/hwp/htmlReader.js +602 -0
- package/dist/lib/hwp/hwpxBuilder.d.ts +19 -0
- package/dist/lib/hwp/hwpxBuilder.js +633 -0
- package/dist/lib/hwp/index.d.ts +68 -0
- package/dist/lib/hwp/index.js +149 -0
- package/dist/lib/hwp/mdReader.d.ts +16 -0
- package/dist/lib/hwp/mdReader.js +485 -0
- package/dist/lib/hwp/mdWriter.d.ts +23 -0
- package/dist/lib/hwp/mdWriter.js +182 -0
- package/dist/lib/hwp/owpml.d.ts +33 -0
- package/dist/lib/hwp/owpml.js +86 -0
- package/dist/lib/hwp/record.d.ts +24 -0
- package/dist/lib/hwp/record.js +59 -0
- package/dist/lib/hwp/tags.d.ts +115 -0
- package/dist/lib/hwp/tags.js +217 -0
- package/dist/lib/hwp/types.d.ts +214 -0
- package/dist/lib/hwp/types.js +5 -0
- package/dist/lib/hwpxReader.d.ts +60 -0
- package/dist/lib/hwpxReader.js +1104 -0
- package/dist/lib/types.d.ts +47 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/writer.d.ts +19 -0
- package/dist/lib/writer.js +149 -0
- package/package.json +94 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OWPML(HWPX) 패키지 생성 공통 헬퍼.
|
|
3
|
+
*
|
|
4
|
+
* writer.ts(평문) 와 hwpxBuilder.ts(IR) 가 공유하는 OWPML 컨벤션 상수·유틸.
|
|
5
|
+
* 기준: 한컴 정상 출력 샘플 etc/hwpxcore_test/ (header.xml / section0.xml).
|
|
6
|
+
*
|
|
7
|
+
* OWPML 핵심 규칙:
|
|
8
|
+
* - mimetype = "application/hwp+zip" (한글의 HWPX 매직 문자열)
|
|
9
|
+
* - 요소만 네임스페이스 prefix(hp:/hh:/hc:), 속성은 prefix 없음
|
|
10
|
+
* - head/sec/package 루트에 풀 네임스페이스 선언
|
|
11
|
+
* - 첫 문단에 <hp:secPr> (페이지 설정) 필수
|
|
12
|
+
*
|
|
13
|
+
* 순수 문자열·로직만 — Node 전용 API(Buffer/fs/path/crypto) 금지 (브라우저 ESM 번들 유지).
|
|
14
|
+
* [shyang 2026-06-21]
|
|
15
|
+
*/
|
|
16
|
+
/** mimetype 파일 내용 — 한글이 HWPX 를 감지하는 매직 문자열. */
|
|
17
|
+
export declare const MIMETYPE = "application/hwp+zip";
|
|
18
|
+
/** head/sec/package 루트가 공유하는 OWPML 풀 네임스페이스 선언. */
|
|
19
|
+
export declare const OWPML_NS: string;
|
|
20
|
+
/** 한 문단 라인세그 기본값(한컴 호환). 속성 prefix 없음. */
|
|
21
|
+
export declare const DEFAULT_LINESEG: string;
|
|
22
|
+
/**
|
|
23
|
+
* 섹션 첫 문단에 들어가는 <hp:secPr>(페이지 설정) + <hp:ctrl><hp:colPr> 블록.
|
|
24
|
+
* 한컴 정상 샘플(etc/hwpxcore_test/Contents/section0.xml) 기준 A4 세로.
|
|
25
|
+
* 한글이 열 때 실제 레이아웃을 재계산하므로 이 기본값으로 충분.
|
|
26
|
+
*/
|
|
27
|
+
export declare const SEC_PR_XML: string;
|
|
28
|
+
/**
|
|
29
|
+
* 문단 고유 id 생성. 한컴은 <hp:p> 에 고유 정수 id 를 요구한다.
|
|
30
|
+
* 결정적(카운터 기반) — 같은 문서/세션 내 고유하면 충분.
|
|
31
|
+
*/
|
|
32
|
+
export declare function makeParaId(): number;
|
|
33
|
+
export declare function escapeXml(s: string): string;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OWPML(HWPX) 패키지 생성 공통 헬퍼.
|
|
3
|
+
*
|
|
4
|
+
* writer.ts(평문) 와 hwpxBuilder.ts(IR) 가 공유하는 OWPML 컨벤션 상수·유틸.
|
|
5
|
+
* 기준: 한컴 정상 출력 샘플 etc/hwpxcore_test/ (header.xml / section0.xml).
|
|
6
|
+
*
|
|
7
|
+
* OWPML 핵심 규칙:
|
|
8
|
+
* - mimetype = "application/hwp+zip" (한글의 HWPX 매직 문자열)
|
|
9
|
+
* - 요소만 네임스페이스 prefix(hp:/hh:/hc:), 속성은 prefix 없음
|
|
10
|
+
* - head/sec/package 루트에 풀 네임스페이스 선언
|
|
11
|
+
* - 첫 문단에 <hp:secPr> (페이지 설정) 필수
|
|
12
|
+
*
|
|
13
|
+
* 순수 문자열·로직만 — Node 전용 API(Buffer/fs/path/crypto) 금지 (브라우저 ESM 번들 유지).
|
|
14
|
+
* [shyang 2026-06-21]
|
|
15
|
+
*/
|
|
16
|
+
/** mimetype 파일 내용 — 한글이 HWPX 를 감지하는 매직 문자열. */
|
|
17
|
+
export const MIMETYPE = "application/hwp+zip";
|
|
18
|
+
/** head/sec/package 루트가 공유하는 OWPML 풀 네임스페이스 선언. */
|
|
19
|
+
export const OWPML_NS = `xmlns:ha="http://www.hancom.co.kr/hwpml/2011/app" ` +
|
|
20
|
+
`xmlns:hp="http://www.hancom.co.kr/hwpml/2011/paragraph" ` +
|
|
21
|
+
`xmlns:hp10="http://www.hancom.co.kr/hwpml/2016/paragraph" ` +
|
|
22
|
+
`xmlns:hs="http://www.hancom.co.kr/hwpml/2011/section" ` +
|
|
23
|
+
`xmlns:hc="http://www.hancom.co.kr/hwpml/2011/core" ` +
|
|
24
|
+
`xmlns:hh="http://www.hancom.co.kr/hwpml/2011/head" ` +
|
|
25
|
+
`xmlns:hhs="http://www.hancom.co.kr/hwpml/2011/history" ` +
|
|
26
|
+
`xmlns:hm="http://www.hancom.co.kr/hwpml/2011/master-page" ` +
|
|
27
|
+
`xmlns:hpf="http://www.hancom.co.kr/schema/2011/hpf" ` +
|
|
28
|
+
`xmlns:dc="http://purl.org/dc/elements/1.1/" ` +
|
|
29
|
+
`xmlns:opf="http://www.idpf.org/2007/opf/" ` +
|
|
30
|
+
`xmlns:ooxmlchart="http://www.hancom.co.kr/hwpml/2016/ooxmlchart" ` +
|
|
31
|
+
`xmlns:hwpunitchar="http://www.hancom.co.kr/hwpml/2016/HwpUnitChar" ` +
|
|
32
|
+
`xmlns:epub="http://www.idpf.org/2007/ops" ` +
|
|
33
|
+
`xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0"`;
|
|
34
|
+
/** 한 문단 라인세그 기본값(한컴 호환). 속성 prefix 없음. */
|
|
35
|
+
export const DEFAULT_LINESEG = `<hp:linesegarray>` +
|
|
36
|
+
`<hp:lineseg textpos="0" vertpos="0" vertsize="1000" textheight="1000" baseline="850" spacing="600" horzpos="0" horzsize="42520" flags="393216"/>` +
|
|
37
|
+
`</hp:linesegarray>`;
|
|
38
|
+
/**
|
|
39
|
+
* 섹션 첫 문단에 들어가는 <hp:secPr>(페이지 설정) + <hp:ctrl><hp:colPr> 블록.
|
|
40
|
+
* 한컴 정상 샘플(etc/hwpxcore_test/Contents/section0.xml) 기준 A4 세로.
|
|
41
|
+
* 한글이 열 때 실제 레이아웃을 재계산하므로 이 기본값으로 충분.
|
|
42
|
+
*/
|
|
43
|
+
export const SEC_PR_XML = `<hp:secPr id="" textDirection="HORIZONTAL" spaceColumns="1134" tabStop="8000" tabStopVal="4000" tabStopUnit="HWPUNIT" outlineShapeIDRef="1" memoShapeIDRef="0" textVerticalWidthHead="0" masterPageCnt="0">` +
|
|
44
|
+
`<hp:grid lineGrid="0" charGrid="0" wonggojiFormat="0"/>` +
|
|
45
|
+
`<hp:startNum pageStartsOn="BOTH" page="0" pic="0" tbl="0" equation="0"/>` +
|
|
46
|
+
`<hp:visibility hideFirstHeader="0" hideFirstFooter="0" hideFirstMasterPage="0" border="SHOW_ALL" fill="SHOW_ALL" hideFirstPageNum="0" hideFirstEmptyLine="0" showLineNumber="0"/>` +
|
|
47
|
+
`<hp:lineNumberShape restartType="0" countBy="0" distance="0" startNumber="0"/>` +
|
|
48
|
+
`<hp:pagePr landscape="WIDELY" width="59528" height="84186" gutterType="LEFT_ONLY">` +
|
|
49
|
+
`<hp:margin header="4252" footer="4252" gutter="0" left="8504" right="8504" top="5668" bottom="4252"/>` +
|
|
50
|
+
`</hp:pagePr>` +
|
|
51
|
+
`<hp:footNotePr>` +
|
|
52
|
+
`<hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/>` +
|
|
53
|
+
`<hp:noteLine length="-1" type="SOLID" width="0.12 mm" color="#000000"/>` +
|
|
54
|
+
`<hp:noteSpacing betweenNotes="283" belowLine="567" aboveLine="850"/>` +
|
|
55
|
+
`<hp:numbering type="CONTINUOUS" newNum="1"/>` +
|
|
56
|
+
`<hp:placement place="EACH_COLUMN" beneathText="0"/>` +
|
|
57
|
+
`</hp:footNotePr>` +
|
|
58
|
+
`<hp:endNotePr>` +
|
|
59
|
+
`<hp:autoNumFormat type="DIGIT" userChar="" prefixChar="" suffixChar=")" supscript="0"/>` +
|
|
60
|
+
`<hp:noteLine length="14692344" type="SOLID" width="0.12 mm" color="#000000"/>` +
|
|
61
|
+
`<hp:noteSpacing betweenNotes="0" belowLine="567" aboveLine="850"/>` +
|
|
62
|
+
`<hp:numbering type="CONTINUOUS" newNum="1"/>` +
|
|
63
|
+
`<hp:placement place="END_OF_DOCUMENT" beneathText="0"/>` +
|
|
64
|
+
`</hp:endNotePr>` +
|
|
65
|
+
`<hp:pageBorderFill type="BOTH" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill>` +
|
|
66
|
+
`<hp:pageBorderFill type="EVEN" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill>` +
|
|
67
|
+
`<hp:pageBorderFill type="ODD" borderFillIDRef="1" textBorder="PAPER" headerInside="0" footerInside="0" fillArea="PAPER"><hp:offset left="1417" right="1417" top="1417" bottom="1417"/></hp:pageBorderFill>` +
|
|
68
|
+
`</hp:secPr>` +
|
|
69
|
+
`<hp:ctrl><hp:colPr id="" type="NEWSPAPER" layout="LEFT" colCount="1" sameSz="1" sameGap="0"/></hp:ctrl>`;
|
|
70
|
+
let paraIdCounter = 0;
|
|
71
|
+
/**
|
|
72
|
+
* 문단 고유 id 생성. 한컴은 <hp:p> 에 고유 정수 id 를 요구한다.
|
|
73
|
+
* 결정적(카운터 기반) — 같은 문서/세션 내 고유하면 충분.
|
|
74
|
+
*/
|
|
75
|
+
export function makeParaId() {
|
|
76
|
+
paraIdCounter = (paraIdCounter + 1) >>> 0;
|
|
77
|
+
return paraIdCounter;
|
|
78
|
+
}
|
|
79
|
+
export function escapeXml(s) {
|
|
80
|
+
return s
|
|
81
|
+
.replace(/&/g, "&")
|
|
82
|
+
.replace(/</g, "<")
|
|
83
|
+
.replace(/>/g, ">")
|
|
84
|
+
.replace(/"/g, """)
|
|
85
|
+
.replace(/'/g, "'");
|
|
86
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HWP 레코드 헤더 (4바이트):
|
|
3
|
+
* - bits 0..9 : tag_id (0..1023)
|
|
4
|
+
* - bits 10..19 : level (0..1023)
|
|
5
|
+
* - bits 20..31 : size (0..4095)
|
|
6
|
+
* - size == 0xFFF 이면 다음 4바이트가 실제 size (확장 크기)
|
|
7
|
+
*
|
|
8
|
+
* 원작: rhwp/src/parser/record.rs (MIT, Copyright (c) 2025-2026 Edward Kim)
|
|
9
|
+
*/
|
|
10
|
+
export interface Record {
|
|
11
|
+
tagId: number;
|
|
12
|
+
level: number;
|
|
13
|
+
size: number;
|
|
14
|
+
data: Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
export declare class RecordError extends Error {
|
|
17
|
+
constructor(msg: string);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 바이트 스트림에서 모든 레코드를 평탄하게 파싱.
|
|
21
|
+
* 트리 재구성은 호출자가 level 필드를 보고 직접 수행.
|
|
22
|
+
*/
|
|
23
|
+
export declare function readAllRecords(data: Uint8Array): Record[];
|
|
24
|
+
export declare function recordTagName(rec: Record): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HWP 레코드 헤더 (4바이트):
|
|
3
|
+
* - bits 0..9 : tag_id (0..1023)
|
|
4
|
+
* - bits 10..19 : level (0..1023)
|
|
5
|
+
* - bits 20..31 : size (0..4095)
|
|
6
|
+
* - size == 0xFFF 이면 다음 4바이트가 실제 size (확장 크기)
|
|
7
|
+
*
|
|
8
|
+
* 원작: rhwp/src/parser/record.rs (MIT, Copyright (c) 2025-2026 Edward Kim)
|
|
9
|
+
*/
|
|
10
|
+
import { tagName } from "./tags.js";
|
|
11
|
+
export class RecordError extends Error {
|
|
12
|
+
constructor(msg) {
|
|
13
|
+
super(msg);
|
|
14
|
+
this.name = "RecordError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 바이트 스트림에서 모든 레코드를 평탄하게 파싱.
|
|
19
|
+
* 트리 재구성은 호출자가 level 필드를 보고 직접 수행.
|
|
20
|
+
*/
|
|
21
|
+
export function readAllRecords(data) {
|
|
22
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
23
|
+
const records = [];
|
|
24
|
+
let offset = 0;
|
|
25
|
+
const end = data.byteLength;
|
|
26
|
+
while (offset < end) {
|
|
27
|
+
if (end - offset < 4) {
|
|
28
|
+
// 트레일링 패딩으로 간주하고 정상 종료
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
const header = view.getUint32(offset, true);
|
|
32
|
+
offset += 4;
|
|
33
|
+
const tagId = header & 0x3ff;
|
|
34
|
+
const level = (header >>> 10) & 0x3ff;
|
|
35
|
+
let size = header >>> 20;
|
|
36
|
+
if (size === 0xfff) {
|
|
37
|
+
if (end - offset < 4) {
|
|
38
|
+
throw new RecordError(`extended size 헤더 중간 EOF (tag=${tagId})`);
|
|
39
|
+
}
|
|
40
|
+
size = view.getUint32(offset, true);
|
|
41
|
+
offset += 4;
|
|
42
|
+
}
|
|
43
|
+
if (offset + size > end) {
|
|
44
|
+
throw new RecordError(`레코드 데이터 부족: tag=${tagId}/${tagName(tagId)}, 필요=${size}, 가용=${end - offset}`);
|
|
45
|
+
}
|
|
46
|
+
const recordData = new Uint8Array(data.buffer, data.byteOffset + offset, size);
|
|
47
|
+
offset += size;
|
|
48
|
+
records.push({
|
|
49
|
+
tagId,
|
|
50
|
+
level,
|
|
51
|
+
size,
|
|
52
|
+
data: new Uint8Array(recordData), // 복사
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return records;
|
|
56
|
+
}
|
|
57
|
+
export function recordTagName(rec) {
|
|
58
|
+
return tagName(rec.tagId);
|
|
59
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HWP 5.0 레코드 TagID 및 컨트롤 ID 상수.
|
|
3
|
+
* HWPTAG_BEGIN(0x010) 기준 오프셋으로 정의.
|
|
4
|
+
*
|
|
5
|
+
* 원작: rhwp/src/parser/tags.rs (MIT, Copyright (c) 2025-2026 Edward Kim)
|
|
6
|
+
*/
|
|
7
|
+
export declare const HWPTAG_BEGIN = 16;
|
|
8
|
+
export declare const HWPTAG_DOCUMENT_PROPERTIES: number;
|
|
9
|
+
export declare const HWPTAG_ID_MAPPINGS: number;
|
|
10
|
+
export declare const HWPTAG_BIN_DATA: number;
|
|
11
|
+
export declare const HWPTAG_FACE_NAME: number;
|
|
12
|
+
export declare const HWPTAG_BORDER_FILL: number;
|
|
13
|
+
export declare const HWPTAG_CHAR_SHAPE: number;
|
|
14
|
+
export declare const HWPTAG_TAB_DEF: number;
|
|
15
|
+
export declare const HWPTAG_NUMBERING: number;
|
|
16
|
+
export declare const HWPTAG_BULLET: number;
|
|
17
|
+
export declare const HWPTAG_PARA_SHAPE: number;
|
|
18
|
+
export declare const HWPTAG_STYLE: number;
|
|
19
|
+
export declare const HWPTAG_DOC_DATA: number;
|
|
20
|
+
export declare const HWPTAG_DISTRIBUTE_DOC_DATA: number;
|
|
21
|
+
export declare const HWPTAG_COMPATIBLE_DOCUMENT: number;
|
|
22
|
+
export declare const HWPTAG_LAYOUT_COMPATIBILITY: number;
|
|
23
|
+
export declare const HWPTAG_TRACKCHANGE: number;
|
|
24
|
+
export declare const HWPTAG_PARA_HEADER: number;
|
|
25
|
+
export declare const HWPTAG_PARA_TEXT: number;
|
|
26
|
+
export declare const HWPTAG_PARA_CHAR_SHAPE: number;
|
|
27
|
+
export declare const HWPTAG_PARA_LINE_SEG: number;
|
|
28
|
+
export declare const HWPTAG_PARA_RANGE_TAG: number;
|
|
29
|
+
export declare const HWPTAG_CTRL_HEADER: number;
|
|
30
|
+
export declare const HWPTAG_LIST_HEADER: number;
|
|
31
|
+
export declare const HWPTAG_PAGE_DEF: number;
|
|
32
|
+
export declare const HWPTAG_FOOTNOTE_SHAPE: number;
|
|
33
|
+
export declare const HWPTAG_PAGE_BORDER_FILL: number;
|
|
34
|
+
export declare const HWPTAG_SHAPE_COMPONENT: number;
|
|
35
|
+
export declare const HWPTAG_TABLE: number;
|
|
36
|
+
export declare const HWPTAG_SHAPE_COMPONENT_LINE: number;
|
|
37
|
+
export declare const HWPTAG_SHAPE_COMPONENT_RECTANGLE: number;
|
|
38
|
+
export declare const HWPTAG_SHAPE_COMPONENT_ELLIPSE: number;
|
|
39
|
+
export declare const HWPTAG_SHAPE_COMPONENT_ARC: number;
|
|
40
|
+
export declare const HWPTAG_SHAPE_COMPONENT_POLYGON: number;
|
|
41
|
+
export declare const HWPTAG_SHAPE_COMPONENT_CURVE: number;
|
|
42
|
+
export declare const HWPTAG_SHAPE_COMPONENT_OLE: number;
|
|
43
|
+
export declare const HWPTAG_SHAPE_COMPONENT_PICTURE: number;
|
|
44
|
+
export declare const HWPTAG_SHAPE_COMPONENT_CONTAINER: number;
|
|
45
|
+
export declare const HWPTAG_CTRL_DATA: number;
|
|
46
|
+
export declare const HWPTAG_EQEDIT: number;
|
|
47
|
+
export declare const HWPTAG_SHAPE_COMPONENT_TEXTART: number;
|
|
48
|
+
export declare const HWPTAG_FORM_OBJECT: number;
|
|
49
|
+
export declare const HWPTAG_MEMO_SHAPE: number;
|
|
50
|
+
export declare const HWPTAG_MEMO_LIST: number;
|
|
51
|
+
export declare const HWPTAG_FORBIDDEN_CHAR: number;
|
|
52
|
+
export declare const HWPTAG_CHART_DATA: number;
|
|
53
|
+
export declare const CHAR_SECTION_COLUMN_DEF = 2;
|
|
54
|
+
export declare const CHAR_FIELD_BEGIN = 3;
|
|
55
|
+
export declare const CHAR_FIELD_END = 4;
|
|
56
|
+
export declare const CHAR_INLINE_NON_TEXT = 8;
|
|
57
|
+
export declare const CHAR_TAB = 9;
|
|
58
|
+
export declare const CHAR_LINE_BREAK = 10;
|
|
59
|
+
export declare const CHAR_EXTENDED_CTRL = 11;
|
|
60
|
+
export declare const CHAR_PARA_BREAK = 13;
|
|
61
|
+
export declare const CHAR_NBSPACE = 24;
|
|
62
|
+
export declare const CHAR_FIXED_WIDTH_SPACE = 25;
|
|
63
|
+
export declare const CHAR_HYPHEN = 30;
|
|
64
|
+
export declare const CHAR_FIXED_WIDTH_SPACE_31 = 31;
|
|
65
|
+
/**
|
|
66
|
+
* 인라인 컨트롤 코드는 16비트 자리에 1~2개 등장한다.
|
|
67
|
+
* 길이 1: 일반 문자 / Tab / Line break / Para break
|
|
68
|
+
* 길이 8: ExtendedCtrl 등 (ctrl_id + 4바이트 + 동일 코드 반복)
|
|
69
|
+
*
|
|
70
|
+
* rhwp 기준 8자리(=16바이트)를 차지하는 컨트롤 코드 집합.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isExtendedCtrlChar(code: number): boolean;
|
|
73
|
+
export declare const CTRL_SECTION_DEF: number;
|
|
74
|
+
export declare const CTRL_COLUMN_DEF: number;
|
|
75
|
+
export declare const CTRL_TABLE: number;
|
|
76
|
+
export declare const CTRL_EQUATION: number;
|
|
77
|
+
export declare const CTRL_GEN_SHAPE: number;
|
|
78
|
+
export declare const SHAPE_PICTURE_ID: number;
|
|
79
|
+
export declare const SHAPE_RECT_ID: number;
|
|
80
|
+
export declare const SHAPE_LINE_ID: number;
|
|
81
|
+
export declare const SHAPE_ELLIPSE_ID: number;
|
|
82
|
+
export declare const SHAPE_POLYGON_ID: number;
|
|
83
|
+
export declare const SHAPE_ARC_ID: number;
|
|
84
|
+
export declare const SHAPE_CURVE_ID: number;
|
|
85
|
+
export declare const SHAPE_CONNECTOR_ID: number;
|
|
86
|
+
export declare const CTRL_HEADER: number;
|
|
87
|
+
export declare const CTRL_FOOTER: number;
|
|
88
|
+
export declare const CTRL_FOOTNOTE: number;
|
|
89
|
+
export declare const CTRL_ENDNOTE: number;
|
|
90
|
+
export declare const CTRL_AUTO_NUMBER: number;
|
|
91
|
+
export declare const CTRL_NEW_NUMBER: number;
|
|
92
|
+
export declare const CTRL_PAGE_NUM_POS: number;
|
|
93
|
+
export declare const CTRL_PAGE_HIDE: number;
|
|
94
|
+
export declare const CTRL_INDEX_MARK: number;
|
|
95
|
+
export declare const CTRL_BOOKMARK: number;
|
|
96
|
+
export declare const CTRL_TCPS: number;
|
|
97
|
+
export declare const CTRL_FORM: number;
|
|
98
|
+
export declare const CTRL_CHAR_OVERLAP: number;
|
|
99
|
+
export declare const CTRL_HIDDEN_COMMENT: number;
|
|
100
|
+
export declare const FIELD_CLICKHERE: number;
|
|
101
|
+
export declare const FIELD_HYPERLINK: number;
|
|
102
|
+
export declare const FIELD_BOOKMARK: number;
|
|
103
|
+
export declare const FIELD_DATE: number;
|
|
104
|
+
export declare const FIELD_DOCDATE: number;
|
|
105
|
+
export declare const FIELD_PATH: number;
|
|
106
|
+
export declare const FIELD_MAILMERGE: number;
|
|
107
|
+
export declare const FIELD_CROSSREF: number;
|
|
108
|
+
export declare const FIELD_FORMULA: number;
|
|
109
|
+
export declare const FIELD_SUMMARY: number;
|
|
110
|
+
export declare const FIELD_USERINFO: number;
|
|
111
|
+
export declare function isFieldCtrlId(id: number): boolean;
|
|
112
|
+
export declare function tagName(tagId: number): string;
|
|
113
|
+
export declare function ctrlName(id: number): string;
|
|
114
|
+
/** ctrl_id를 4글자 문자열로 (디버그 용) */
|
|
115
|
+
export declare function ctrlIdToString(id: number): string;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HWP 5.0 레코드 TagID 및 컨트롤 ID 상수.
|
|
3
|
+
* HWPTAG_BEGIN(0x010) 기준 오프셋으로 정의.
|
|
4
|
+
*
|
|
5
|
+
* 원작: rhwp/src/parser/tags.rs (MIT, Copyright (c) 2025-2026 Edward Kim)
|
|
6
|
+
*/
|
|
7
|
+
export const HWPTAG_BEGIN = 0x010;
|
|
8
|
+
// ============================================================
|
|
9
|
+
// DocInfo 태그 (HWPTAG_BEGIN + 0 ~ 49)
|
|
10
|
+
// ============================================================
|
|
11
|
+
export const HWPTAG_DOCUMENT_PROPERTIES = HWPTAG_BEGIN + 0;
|
|
12
|
+
export const HWPTAG_ID_MAPPINGS = HWPTAG_BEGIN + 1;
|
|
13
|
+
export const HWPTAG_BIN_DATA = HWPTAG_BEGIN + 2;
|
|
14
|
+
export const HWPTAG_FACE_NAME = HWPTAG_BEGIN + 3;
|
|
15
|
+
export const HWPTAG_BORDER_FILL = HWPTAG_BEGIN + 4;
|
|
16
|
+
export const HWPTAG_CHAR_SHAPE = HWPTAG_BEGIN + 5;
|
|
17
|
+
export const HWPTAG_TAB_DEF = HWPTAG_BEGIN + 6;
|
|
18
|
+
export const HWPTAG_NUMBERING = HWPTAG_BEGIN + 7;
|
|
19
|
+
export const HWPTAG_BULLET = HWPTAG_BEGIN + 8;
|
|
20
|
+
export const HWPTAG_PARA_SHAPE = HWPTAG_BEGIN + 9;
|
|
21
|
+
export const HWPTAG_STYLE = HWPTAG_BEGIN + 10;
|
|
22
|
+
export const HWPTAG_DOC_DATA = HWPTAG_BEGIN + 11;
|
|
23
|
+
export const HWPTAG_DISTRIBUTE_DOC_DATA = HWPTAG_BEGIN + 12;
|
|
24
|
+
// (HWPTAG_BEGIN + 13 예약)
|
|
25
|
+
export const HWPTAG_COMPATIBLE_DOCUMENT = HWPTAG_BEGIN + 14;
|
|
26
|
+
export const HWPTAG_LAYOUT_COMPATIBILITY = HWPTAG_BEGIN + 15;
|
|
27
|
+
export const HWPTAG_TRACKCHANGE = HWPTAG_BEGIN + 16;
|
|
28
|
+
// ============================================================
|
|
29
|
+
// BodyText 태그 (HWPTAG_BEGIN + 50 ~)
|
|
30
|
+
// ============================================================
|
|
31
|
+
export const HWPTAG_PARA_HEADER = HWPTAG_BEGIN + 50;
|
|
32
|
+
export const HWPTAG_PARA_TEXT = HWPTAG_BEGIN + 51;
|
|
33
|
+
export const HWPTAG_PARA_CHAR_SHAPE = HWPTAG_BEGIN + 52;
|
|
34
|
+
export const HWPTAG_PARA_LINE_SEG = HWPTAG_BEGIN + 53;
|
|
35
|
+
export const HWPTAG_PARA_RANGE_TAG = HWPTAG_BEGIN + 54;
|
|
36
|
+
export const HWPTAG_CTRL_HEADER = HWPTAG_BEGIN + 55;
|
|
37
|
+
export const HWPTAG_LIST_HEADER = HWPTAG_BEGIN + 56;
|
|
38
|
+
export const HWPTAG_PAGE_DEF = HWPTAG_BEGIN + 57;
|
|
39
|
+
export const HWPTAG_FOOTNOTE_SHAPE = HWPTAG_BEGIN + 58;
|
|
40
|
+
export const HWPTAG_PAGE_BORDER_FILL = HWPTAG_BEGIN + 59;
|
|
41
|
+
export const HWPTAG_SHAPE_COMPONENT = HWPTAG_BEGIN + 60;
|
|
42
|
+
export const HWPTAG_TABLE = HWPTAG_BEGIN + 61;
|
|
43
|
+
export const HWPTAG_SHAPE_COMPONENT_LINE = HWPTAG_BEGIN + 62;
|
|
44
|
+
export const HWPTAG_SHAPE_COMPONENT_RECTANGLE = HWPTAG_BEGIN + 63;
|
|
45
|
+
export const HWPTAG_SHAPE_COMPONENT_ELLIPSE = HWPTAG_BEGIN + 64;
|
|
46
|
+
export const HWPTAG_SHAPE_COMPONENT_ARC = HWPTAG_BEGIN + 65;
|
|
47
|
+
export const HWPTAG_SHAPE_COMPONENT_POLYGON = HWPTAG_BEGIN + 66;
|
|
48
|
+
export const HWPTAG_SHAPE_COMPONENT_CURVE = HWPTAG_BEGIN + 67;
|
|
49
|
+
export const HWPTAG_SHAPE_COMPONENT_OLE = HWPTAG_BEGIN + 68;
|
|
50
|
+
export const HWPTAG_SHAPE_COMPONENT_PICTURE = HWPTAG_BEGIN + 69;
|
|
51
|
+
export const HWPTAG_SHAPE_COMPONENT_CONTAINER = HWPTAG_BEGIN + 70;
|
|
52
|
+
export const HWPTAG_CTRL_DATA = HWPTAG_BEGIN + 71;
|
|
53
|
+
export const HWPTAG_EQEDIT = HWPTAG_BEGIN + 72;
|
|
54
|
+
// (HWPTAG_BEGIN + 73 예약)
|
|
55
|
+
export const HWPTAG_SHAPE_COMPONENT_TEXTART = HWPTAG_BEGIN + 74;
|
|
56
|
+
export const HWPTAG_FORM_OBJECT = HWPTAG_BEGIN + 75;
|
|
57
|
+
export const HWPTAG_MEMO_SHAPE = HWPTAG_BEGIN + 76;
|
|
58
|
+
export const HWPTAG_MEMO_LIST = HWPTAG_BEGIN + 77;
|
|
59
|
+
export const HWPTAG_FORBIDDEN_CHAR = HWPTAG_BEGIN + 78;
|
|
60
|
+
export const HWPTAG_CHART_DATA = HWPTAG_BEGIN + 79;
|
|
61
|
+
// ============================================================
|
|
62
|
+
// 인라인 컨트롤 코드 (텍스트 내 특수 문자 — UTF-16 코드 포인트)
|
|
63
|
+
// ============================================================
|
|
64
|
+
export const CHAR_SECTION_COLUMN_DEF = 0x0002;
|
|
65
|
+
export const CHAR_FIELD_BEGIN = 0x0003;
|
|
66
|
+
export const CHAR_FIELD_END = 0x0004;
|
|
67
|
+
export const CHAR_INLINE_NON_TEXT = 0x0008;
|
|
68
|
+
export const CHAR_TAB = 0x0009;
|
|
69
|
+
export const CHAR_LINE_BREAK = 0x000a;
|
|
70
|
+
export const CHAR_EXTENDED_CTRL = 0x000b;
|
|
71
|
+
export const CHAR_PARA_BREAK = 0x000d;
|
|
72
|
+
export const CHAR_NBSPACE = 0x0018;
|
|
73
|
+
export const CHAR_FIXED_WIDTH_SPACE = 0x0019;
|
|
74
|
+
export const CHAR_HYPHEN = 0x001e;
|
|
75
|
+
export const CHAR_FIXED_WIDTH_SPACE_31 = 0x001f;
|
|
76
|
+
/**
|
|
77
|
+
* 인라인 컨트롤 코드는 16비트 자리에 1~2개 등장한다.
|
|
78
|
+
* 길이 1: 일반 문자 / Tab / Line break / Para break
|
|
79
|
+
* 길이 8: ExtendedCtrl 등 (ctrl_id + 4바이트 + 동일 코드 반복)
|
|
80
|
+
*
|
|
81
|
+
* rhwp 기준 8자리(=16바이트)를 차지하는 컨트롤 코드 집합.
|
|
82
|
+
*/
|
|
83
|
+
export function isExtendedCtrlChar(code) {
|
|
84
|
+
return (code === CHAR_SECTION_COLUMN_DEF ||
|
|
85
|
+
code === CHAR_FIELD_BEGIN ||
|
|
86
|
+
code === CHAR_FIELD_END ||
|
|
87
|
+
code === CHAR_INLINE_NON_TEXT ||
|
|
88
|
+
code === CHAR_EXTENDED_CTRL);
|
|
89
|
+
}
|
|
90
|
+
// ============================================================
|
|
91
|
+
// 컨트롤 ID (4바이트 ASCII → u32, big-endian 인코딩)
|
|
92
|
+
// ============================================================
|
|
93
|
+
function ctrlId(s) {
|
|
94
|
+
if (s.length !== 4)
|
|
95
|
+
throw new Error(`ctrlId requires 4 chars, got "${s}"`);
|
|
96
|
+
return ((s.charCodeAt(0) << 24) | (s.charCodeAt(1) << 16) | (s.charCodeAt(2) << 8) | s.charCodeAt(3)) >>> 0;
|
|
97
|
+
}
|
|
98
|
+
export const CTRL_SECTION_DEF = ctrlId("secd");
|
|
99
|
+
export const CTRL_COLUMN_DEF = ctrlId("cold");
|
|
100
|
+
export const CTRL_TABLE = ctrlId("tbl ");
|
|
101
|
+
export const CTRL_EQUATION = ctrlId("eqed");
|
|
102
|
+
export const CTRL_GEN_SHAPE = ctrlId("gso ");
|
|
103
|
+
export const SHAPE_PICTURE_ID = ctrlId("$pic");
|
|
104
|
+
export const SHAPE_RECT_ID = ctrlId("$rec");
|
|
105
|
+
export const SHAPE_LINE_ID = ctrlId("$lin");
|
|
106
|
+
export const SHAPE_ELLIPSE_ID = ctrlId("$ell");
|
|
107
|
+
export const SHAPE_POLYGON_ID = ctrlId("$pol");
|
|
108
|
+
export const SHAPE_ARC_ID = ctrlId("$arc");
|
|
109
|
+
export const SHAPE_CURVE_ID = ctrlId("$cur");
|
|
110
|
+
export const SHAPE_CONNECTOR_ID = ctrlId("$col");
|
|
111
|
+
export const CTRL_HEADER = ctrlId("head");
|
|
112
|
+
export const CTRL_FOOTER = ctrlId("foot");
|
|
113
|
+
export const CTRL_FOOTNOTE = ctrlId("fn ");
|
|
114
|
+
export const CTRL_ENDNOTE = ctrlId("en ");
|
|
115
|
+
export const CTRL_AUTO_NUMBER = ctrlId("atno");
|
|
116
|
+
export const CTRL_NEW_NUMBER = ctrlId("nwno");
|
|
117
|
+
export const CTRL_PAGE_NUM_POS = ctrlId("pgnp");
|
|
118
|
+
export const CTRL_PAGE_HIDE = ctrlId("pghd");
|
|
119
|
+
export const CTRL_INDEX_MARK = ctrlId("idxm");
|
|
120
|
+
export const CTRL_BOOKMARK = ctrlId("bokm");
|
|
121
|
+
export const CTRL_TCPS = ctrlId("tcps");
|
|
122
|
+
export const CTRL_FORM = ctrlId("form");
|
|
123
|
+
export const CTRL_CHAR_OVERLAP = ctrlId("tdut");
|
|
124
|
+
export const CTRL_HIDDEN_COMMENT = ctrlId("tcmt");
|
|
125
|
+
// 필드 컨트롤 (% 접두어)
|
|
126
|
+
export const FIELD_CLICKHERE = ctrlId("%clk");
|
|
127
|
+
export const FIELD_HYPERLINK = ctrlId("%hlk");
|
|
128
|
+
export const FIELD_BOOKMARK = ctrlId("%bmk");
|
|
129
|
+
export const FIELD_DATE = ctrlId("%dte");
|
|
130
|
+
export const FIELD_DOCDATE = ctrlId("%ddt");
|
|
131
|
+
export const FIELD_PATH = ctrlId("%pat");
|
|
132
|
+
export const FIELD_MAILMERGE = ctrlId("%mmg");
|
|
133
|
+
export const FIELD_CROSSREF = ctrlId("%xrf");
|
|
134
|
+
export const FIELD_FORMULA = ctrlId("%fmu");
|
|
135
|
+
export const FIELD_SUMMARY = ctrlId("%smr");
|
|
136
|
+
export const FIELD_USERINFO = ctrlId("%usr");
|
|
137
|
+
export function isFieldCtrlId(id) {
|
|
138
|
+
return ((id >>> 24) & 0xff) === 0x25; // '%'
|
|
139
|
+
}
|
|
140
|
+
const TAG_NAMES = {
|
|
141
|
+
[HWPTAG_DOCUMENT_PROPERTIES]: "DOCUMENT_PROPERTIES",
|
|
142
|
+
[HWPTAG_ID_MAPPINGS]: "ID_MAPPINGS",
|
|
143
|
+
[HWPTAG_BIN_DATA]: "BIN_DATA",
|
|
144
|
+
[HWPTAG_FACE_NAME]: "FACE_NAME",
|
|
145
|
+
[HWPTAG_BORDER_FILL]: "BORDER_FILL",
|
|
146
|
+
[HWPTAG_CHAR_SHAPE]: "CHAR_SHAPE",
|
|
147
|
+
[HWPTAG_TAB_DEF]: "TAB_DEF",
|
|
148
|
+
[HWPTAG_NUMBERING]: "NUMBERING",
|
|
149
|
+
[HWPTAG_BULLET]: "BULLET",
|
|
150
|
+
[HWPTAG_PARA_SHAPE]: "PARA_SHAPE",
|
|
151
|
+
[HWPTAG_STYLE]: "STYLE",
|
|
152
|
+
[HWPTAG_DOC_DATA]: "DOC_DATA",
|
|
153
|
+
[HWPTAG_DISTRIBUTE_DOC_DATA]: "DISTRIBUTE_DOC_DATA",
|
|
154
|
+
[HWPTAG_COMPATIBLE_DOCUMENT]: "COMPATIBLE_DOCUMENT",
|
|
155
|
+
[HWPTAG_LAYOUT_COMPATIBILITY]: "LAYOUT_COMPATIBILITY",
|
|
156
|
+
[HWPTAG_TRACKCHANGE]: "TRACKCHANGE",
|
|
157
|
+
[HWPTAG_PARA_HEADER]: "PARA_HEADER",
|
|
158
|
+
[HWPTAG_PARA_TEXT]: "PARA_TEXT",
|
|
159
|
+
[HWPTAG_PARA_CHAR_SHAPE]: "PARA_CHAR_SHAPE",
|
|
160
|
+
[HWPTAG_PARA_LINE_SEG]: "PARA_LINE_SEG",
|
|
161
|
+
[HWPTAG_PARA_RANGE_TAG]: "PARA_RANGE_TAG",
|
|
162
|
+
[HWPTAG_CTRL_HEADER]: "CTRL_HEADER",
|
|
163
|
+
[HWPTAG_LIST_HEADER]: "LIST_HEADER",
|
|
164
|
+
[HWPTAG_PAGE_DEF]: "PAGE_DEF",
|
|
165
|
+
[HWPTAG_FOOTNOTE_SHAPE]: "FOOTNOTE_SHAPE",
|
|
166
|
+
[HWPTAG_PAGE_BORDER_FILL]: "PAGE_BORDER_FILL",
|
|
167
|
+
[HWPTAG_SHAPE_COMPONENT]: "SHAPE_COMPONENT",
|
|
168
|
+
[HWPTAG_TABLE]: "TABLE",
|
|
169
|
+
[HWPTAG_SHAPE_COMPONENT_LINE]: "SHAPE_LINE",
|
|
170
|
+
[HWPTAG_SHAPE_COMPONENT_RECTANGLE]: "SHAPE_RECTANGLE",
|
|
171
|
+
[HWPTAG_SHAPE_COMPONENT_ELLIPSE]: "SHAPE_ELLIPSE",
|
|
172
|
+
[HWPTAG_SHAPE_COMPONENT_ARC]: "SHAPE_ARC",
|
|
173
|
+
[HWPTAG_SHAPE_COMPONENT_POLYGON]: "SHAPE_POLYGON",
|
|
174
|
+
[HWPTAG_SHAPE_COMPONENT_CURVE]: "SHAPE_CURVE",
|
|
175
|
+
[HWPTAG_SHAPE_COMPONENT_OLE]: "SHAPE_OLE",
|
|
176
|
+
[HWPTAG_SHAPE_COMPONENT_PICTURE]: "SHAPE_PICTURE",
|
|
177
|
+
[HWPTAG_SHAPE_COMPONENT_CONTAINER]: "SHAPE_CONTAINER",
|
|
178
|
+
[HWPTAG_CTRL_DATA]: "CTRL_DATA",
|
|
179
|
+
[HWPTAG_EQEDIT]: "EQEDIT",
|
|
180
|
+
[HWPTAG_SHAPE_COMPONENT_TEXTART]: "SHAPE_TEXTART",
|
|
181
|
+
[HWPTAG_FORM_OBJECT]: "FORM_OBJECT",
|
|
182
|
+
[HWPTAG_MEMO_SHAPE]: "MEMO_SHAPE",
|
|
183
|
+
[HWPTAG_MEMO_LIST]: "MEMO_LIST",
|
|
184
|
+
[HWPTAG_FORBIDDEN_CHAR]: "FORBIDDEN_CHAR",
|
|
185
|
+
[HWPTAG_CHART_DATA]: "CHART_DATA",
|
|
186
|
+
};
|
|
187
|
+
export function tagName(tagId) {
|
|
188
|
+
return TAG_NAMES[tagId] ?? "UNKNOWN";
|
|
189
|
+
}
|
|
190
|
+
const CTRL_NAMES = {
|
|
191
|
+
[CTRL_SECTION_DEF]: "SectionDef",
|
|
192
|
+
[CTRL_COLUMN_DEF]: "ColumnDef",
|
|
193
|
+
[CTRL_TABLE]: "Table",
|
|
194
|
+
[CTRL_EQUATION]: "Equation",
|
|
195
|
+
[CTRL_GEN_SHAPE]: "GenShape",
|
|
196
|
+
[CTRL_HEADER]: "Header",
|
|
197
|
+
[CTRL_FOOTER]: "Footer",
|
|
198
|
+
[CTRL_FOOTNOTE]: "Footnote",
|
|
199
|
+
[CTRL_ENDNOTE]: "Endnote",
|
|
200
|
+
[CTRL_AUTO_NUMBER]: "AutoNumber",
|
|
201
|
+
[CTRL_NEW_NUMBER]: "NewNumber",
|
|
202
|
+
[CTRL_PAGE_NUM_POS]: "PageNumPos",
|
|
203
|
+
[CTRL_PAGE_HIDE]: "PageHide",
|
|
204
|
+
[CTRL_INDEX_MARK]: "IndexMark",
|
|
205
|
+
[CTRL_BOOKMARK]: "Bookmark",
|
|
206
|
+
[CTRL_TCPS]: "Tcps",
|
|
207
|
+
[CTRL_FORM]: "Form",
|
|
208
|
+
[CTRL_CHAR_OVERLAP]: "CharOverlap",
|
|
209
|
+
[CTRL_HIDDEN_COMMENT]: "HiddenComment",
|
|
210
|
+
};
|
|
211
|
+
export function ctrlName(id) {
|
|
212
|
+
return CTRL_NAMES[id] ?? "Unknown";
|
|
213
|
+
}
|
|
214
|
+
/** ctrl_id를 4글자 문자열로 (디버그 용) */
|
|
215
|
+
export function ctrlIdToString(id) {
|
|
216
|
+
return String.fromCharCode((id >>> 24) & 0xff, (id >>> 16) & 0xff, (id >>> 8) & 0xff, id & 0xff);
|
|
217
|
+
}
|