Each BodyText/Sectionn stream is a tree of
records. Every paragraph
is a HWPTAG_PARA_HEADER at some level, with its text, character shapes,
line layout and controls as children one level deeper. A control (a table, a picture,
a footnote) has its own records, and paragraphs inside it hang below a list header.
The first paragraph of a section holds the section and column definitions as
controls. This page follows Hancom’s HWP 5.0 specification, revision 1.3 (sections
3.2.3 and 4.3.1 to 4.3.5), with its table numbers; the controls themselves are on the
controls and
objects pages.
Record tree
PARA_HEADER level 0
PARA_TEXT level 1
PARA_CHAR_SHAPE level 1
PARA_LINE_SEG level 1
PARA_RANGE_TAG level 1 (optional)
CTRL_HEADER 'tbl ' level 1 (one per extended control in the text)
TABLE level 2
LIST_HEADER level 2 (one per cell)
PARA_HEADER level 2 (the cell's paragraphs)
PARA_TEXT level 3
…
HWPTAG_PARA_HEADER
| Offset | Type | Field | Meaning |
|---|---|---|---|
0 | UINT32 | Text length | Bits 0–30: number of UTF-16 units in the text. The specification only says to clear bit 31 (if (nchars & 0x80000000) nchars &= 0x7fffffff); what the bit means is not published. |
4 | UINT32 | Control mask | The OR of 1 << ch for each control character ch in the text, so readers can skip paragraphs without controls. |
8 | UINT16 | Paragraph shape | Index of a HWPTAG_PARA_SHAPE in DocInfo. |
10 | UINT8 | Style | Index of a HWPTAG_STYLE. |
11 | UINT8 | Break kind | Bit 0 section break, 1 column definition break, 2 page break, 3 column break, before this paragraph. |
12 | UINT16 | Character shape count | Entries in PARA_CHAR_SHAPE. |
14 | UINT16 | Range tag count | Entries in PARA_RANGE_TAG. |
16 | UINT16 | Line segment count | Entries in PARA_LINE_SEG. |
18 | UINT32 | Instance ID | Unique ID of the paragraph. |
22 | UINT16 | Merged by change tracking | Whether the paragraph was merged by tracked changes (from version 5.0.3.2). |
24 bytes (table 58); files before 5.0.3.2 have the first 22 (the size in the specification’s table 5). Newer versions may make the record longer; readers should use the record size and ignore bytes they do not know. The break kinds are table 59. When the text length is 0, a reader makes the paragraph from a paragraph break alone.
HWPTAG_PARA_TEXT and control characters
The text is UTF-16LE. Code units 0 to 31 are control characters of three kinds:
“char” controls take one unit, like an ordinary character; “inline” and “extended”
controls take eight units (16 bytes): the code, six units of data (an extended control
gives the four-character control ID there and points to a CTRL_HEADER
child record), and the code again.
| Code | Kind | Meaning |
|---|---|---|
| 0 | char | Unusable |
| 1 | extended | Reserved |
| 2 | extended | Section or column definition |
| 3 | extended | Field start (hyperlink, click-here field and so on) |
| 4 | inline | Field end |
| 5–7 | inline | Reserved |
| 8 | inline | Title mark |
| 9 | inline | Tab |
| 10 | char | Line break |
| 11 | extended | Drawing object or table |
| 12 | extended | Reserved |
| 13 | char | Paragraph break (ends every paragraph) |
| 14 | extended | Reserved |
| 15 | extended | Hidden comment |
| 16 | extended | Header or footer |
| 17 | extended | Footnote or endnote |
| 18 | extended | Automatic number (pages, figures, tables…) |
| 19–20 | inline | Reserved |
| 21 | extended | Page control (hiding, starting a new number and so on) |
| 22 | extended | Bookmark or index mark |
| 23 | extended | Overlapping characters or ruby (dutmal) |
| 24 | char | Hyphen |
| 25–29 | char | Reserved |
| 30 | char | Non-breaking space (keeps words together) |
| 31 | char | Fixed-width space |
41 00 42 00 0D 00
The text of a paragraph “AB”: A, B and the paragraph break
(13). An empty paragraph’s text is just 0D 00, and some writers then leave
out the PARA_TEXT record.
HWPTAG_PARA_CHAR_SHAPE and HWPTAG_PARA_LINE_SEG
PARA_CHAR_SHAPE is a list of pairs of UINT32, as many as the header’s
count: the position in the text where a run starts, and the index of the
HWPTAG_CHAR_SHAPE it uses. The first run starts at 0.
PARA_LINE_SEG is the writer’s line layout, 36 bytes per line:
| Offset | Type | Field |
|---|---|---|
0 | UINT32 | Position in the text where the line starts |
4 | INT32 | Vertical position of the line |
8 | INT32 | Line height |
12 | INT32 | Height of the text part |
16 | INT32 | Distance from the line’s vertical position to the baseline |
20 | INT32 | Line spacing |
24 | INT32 | Start position in the column |
28 | INT32 | Width of the segment |
32 | UINT32 | Flags (below) |
| Bit | Meaning when set (table 62) |
|---|---|
| 0 | First line of the page |
| 1 | First line of the column |
| 16 | An empty segment, with no text laid out |
| 17 | First segment of the line |
| 18 | Last segment of the line |
| 19 | Automatic hyphenation was done at the end of the line |
| 20 | Indentation applied |
| 21 | Paragraph head (number or bullet) applied |
| 31 | Used internally, for convenience of the implementation |
This is a cache of how the writer laid out each line; there is one entry per line segment, as many as the paragraph header says. Lengths are in HWPUNIT, 7,200 to the inch (1/100 point).
HWPTAG_PARA_RANGE_TAG
Marks ranges of text, such as highlighting and proofreading marks; unlike character shapes, ranges may overlap. One 12-byte entry per range, as many as the paragraph header says (table 63):
| Offset | Type | Field |
|---|---|---|
0 | UINT32 | Start of the range |
4 | UINT32 | End of the range |
8 | UINT32 | Tag: the top 8 bits are the kind, the low 24 bits data whose meaning depends on the kind |
The kinds are not listed in the specification.
Control IDs
A CTRL_HEADER starts with a four-character ID, stored as a UINT32 whose
bytes read backwards: tbl is stored 20 6C 62 74.
| ID | Control | ID | Control |
|---|---|---|---|
secd | Section definition | cold | Column definition |
tbl | Table | gso | Drawing object (generic shape) |
eqed | Equation | head | Header |
foot | Footer | fn | Footnote |
en | Endnote | atno | Automatic number |
nwno | New number (restart numbering) | pghd | Hide header, footer or page number on this page |
pgct | Odd/even page adjustment | pgnp | Page number position |
idxm | Index mark | bokm | Bookmark |
tcps | Overlapping characters | tdut | Ruby text (dutmal) |
tcmt | Hidden comment |
Drawing objects (gso ) have a second ID in their
SHAPE_COMPONENT record saying which kind they are: $lin
line, $rec rectangle, $ell ellipse, $arc arc,
$pol polygon, $cur curve, $pic picture,
$ole OLE object, $con group (container).
Field IDs
Fields (control code 3) have IDs starting with %:
| ID | Field | ID | Field |
|---|---|---|---|
%unk | Unknown | %dte | Date |
%ddt | Document date | %pat | File path |
%bmk | Bookmark | %mmg | Mail merge |
%xrf | Cross-reference | %fmu | Formula |
%clk | Click here (fill-in) | %smr | Summary information |
%usr | User information | %hlk | Hyperlink |
%sig | Revision: sign | %spl | Revision: split |
%%me | Memo | %cpr | Private information security |
%toc | Table of contents |
Proofreading (revision) marks have IDs starting %%*:
%%*d delete, %%*a attach, %%*C clipping,
%%*S sawtooth, %%*T thinking, %%*P praise,
%%*L line, %%*c simple change, %%*h hyperlink,
%%*A line attach, %%*i line link, %%*t line
transfer, %%*r move right, %%*l move left, %%*n
transfer, %%*e simple insert, and %%mr change. These are the
IDs of the specification’s table 128. The
field control’s fields are on the
controls page.