doc-codec 0.0.0 → 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/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/bytes.cjs +44 -0
- package/dist/bytes.d.cts +9 -0
- package/dist/bytes.d.ts +9 -0
- package/dist/bytes.js +38 -0
- package/dist/detect.cjs +22 -0
- package/dist/detect.d.cts +6 -0
- package/dist/detect.d.ts +6 -0
- package/dist/detect.js +20 -0
- package/dist/errors.cjs +17 -0
- package/dist/errors.d.cts +9 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +15 -0
- package/dist/fib/fib.cjs +50 -0
- package/dist/fib/fib.d.cts +29 -0
- package/dist/fib/fib.d.ts +29 -0
- package/dist/fib/fib.js +48 -0
- package/dist/fib/offsets.cjs +61 -0
- package/dist/fib/offsets.d.cts +47 -0
- package/dist/fib/offsets.d.ts +47 -0
- package/dist/fib/offsets.js +47 -0
- package/dist/index.cjs +79 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/plc.cjs +44 -0
- package/dist/plc.d.cts +12 -0
- package/dist/plc.d.ts +12 -0
- package/dist/plc.js +42 -0
- package/dist/prop/chp.cjs +172 -0
- package/dist/prop/chp.d.cts +16 -0
- package/dist/prop/chp.d.ts +16 -0
- package/dist/prop/chp.js +171 -0
- package/dist/prop/fkp.cjs +141 -0
- package/dist/prop/fkp.d.cts +33 -0
- package/dist/prop/fkp.d.ts +33 -0
- package/dist/prop/fkp.js +137 -0
- package/dist/prop/pap.cjs +134 -0
- package/dist/prop/pap.d.cts +25 -0
- package/dist/prop/pap.d.ts +25 -0
- package/dist/prop/pap.js +133 -0
- package/dist/prop/sprm.cjs +68 -0
- package/dist/prop/sprm.d.cts +26 -0
- package/dist/prop/sprm.d.ts +26 -0
- package/dist/prop/sprm.js +64 -0
- package/dist/read.cjs +171 -0
- package/dist/read.d.cts +12 -0
- package/dist/read.d.ts +12 -0
- package/dist/read.js +169 -0
- package/dist/style/stsh.cjs +65 -0
- package/dist/style/stsh.d.cts +29 -0
- package/dist/style/stsh.d.ts +29 -0
- package/dist/style/stsh.js +61 -0
- package/dist/text/characters.cjs +69 -0
- package/dist/text/characters.d.cts +14 -0
- package/dist/text/characters.d.ts +14 -0
- package/dist/text/characters.js +67 -0
- package/dist/text/piece-table.cjs +68 -0
- package/dist/text/piece-table.d.cts +28 -0
- package/dist/text/piece-table.d.ts +28 -0
- package/dist/text/piece-table.js +65 -0
- package/dist/text/special.cjs +48 -0
- package/dist/text/special.d.cts +30 -0
- package/dist/text/special.d.ts +30 -0
- package/dist/text/special.js +34 -0
- package/package.json +93 -2
package/dist/prop/chp.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { DocFormatError } from "../errors.js";
|
|
2
|
+
import { readUint16LE, readUint8 } from "../bytes.js";
|
|
3
|
+
import { SGC } from "./sprm.js";
|
|
4
|
+
//#region src/prop/chp.ts
|
|
5
|
+
/** sprmCFBold: a ToggleOperand switching bold. */
|
|
6
|
+
const SPRM_C_F_BOLD = 2101;
|
|
7
|
+
/** sprmCFItalic. */
|
|
8
|
+
const SPRM_C_F_ITALIC = 2102;
|
|
9
|
+
/** sprmCFStrike. */
|
|
10
|
+
const SPRM_C_F_STRIKE = 2103;
|
|
11
|
+
/** sprmCKul: a Kul value giving the underline style; anything but 0x00 is some kind of underline. */
|
|
12
|
+
const SPRM_C_KUL = 10814;
|
|
13
|
+
/** sprmCHps: "an unsigned 2-byte integer, in half-points", so points are the operand halved. */
|
|
14
|
+
const SPRM_C_HPS = 19011;
|
|
15
|
+
/** sprmCIstd: the istd of a character style to apply. */
|
|
16
|
+
const SPRM_C_ISTD = 18992;
|
|
17
|
+
/** sprmCIco: an Ico value, an index into [MS-DOC] 2.9.126's fixed 17-entry palette. */
|
|
18
|
+
const SPRM_C_ICO = 10818;
|
|
19
|
+
/** sprmCCv: a COLORREF, the richer colour sprm that supersedes sprmCIco where both appear. */
|
|
20
|
+
const SPRM_C_CV = 26736;
|
|
21
|
+
/** ToggleOperand, [MS-DOC] 2.9.336. 0x80 and 0x81 are relative to the style's own value rather than absolute. */
|
|
22
|
+
const TOGGLE_OFF = 0;
|
|
23
|
+
const TOGGLE_ON = 1;
|
|
24
|
+
const TOGGLE_INHERIT_FROM_STYLE = 128;
|
|
25
|
+
const TOGGLE_INVERT_STYLE = 129;
|
|
26
|
+
const HALF_POINTS_PER_POINT = 2;
|
|
27
|
+
const COLOR_COMPONENT_MAX = 255;
|
|
28
|
+
const ICO_PALETTE = [
|
|
29
|
+
void 0,
|
|
30
|
+
[
|
|
31
|
+
0,
|
|
32
|
+
0,
|
|
33
|
+
0
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
0,
|
|
37
|
+
0,
|
|
38
|
+
255
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
0,
|
|
42
|
+
255,
|
|
43
|
+
255
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
0,
|
|
47
|
+
255,
|
|
48
|
+
0
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
255,
|
|
52
|
+
0,
|
|
53
|
+
255
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
255,
|
|
57
|
+
0,
|
|
58
|
+
0
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
255,
|
|
62
|
+
255,
|
|
63
|
+
0
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
255,
|
|
67
|
+
255,
|
|
68
|
+
255
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
0,
|
|
72
|
+
0,
|
|
73
|
+
128
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
0,
|
|
77
|
+
128,
|
|
78
|
+
128
|
|
79
|
+
],
|
|
80
|
+
[
|
|
81
|
+
0,
|
|
82
|
+
128,
|
|
83
|
+
0
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
128,
|
|
87
|
+
0,
|
|
88
|
+
128
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
128,
|
|
92
|
+
0,
|
|
93
|
+
128
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
128,
|
|
97
|
+
128,
|
|
98
|
+
0
|
|
99
|
+
],
|
|
100
|
+
[
|
|
101
|
+
128,
|
|
102
|
+
128,
|
|
103
|
+
128
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
192,
|
|
107
|
+
192,
|
|
108
|
+
192
|
|
109
|
+
]
|
|
110
|
+
];
|
|
111
|
+
function toggle(operand, current) {
|
|
112
|
+
const value = readUint8(operand, 0);
|
|
113
|
+
switch (value) {
|
|
114
|
+
case TOGGLE_OFF: return false;
|
|
115
|
+
case TOGGLE_ON: return true;
|
|
116
|
+
case TOGGLE_INHERIT_FROM_STYLE: return current ?? false;
|
|
117
|
+
case TOGGLE_INVERT_STYLE: return !(current ?? false);
|
|
118
|
+
default: throw new DocFormatError(`ToggleOperand value 0x${value.toString(16)} is none of the four [MS-DOC] 2.9.336 defines (0x00, 0x01, 0x80, 0x81)`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function icoColor(value) {
|
|
122
|
+
if (value >= ICO_PALETTE.length) throw new DocFormatError(`Ico value 0x${value.toString(16)} is not less than 0x11, the bound [MS-DOC] 2.9.126 places on the palette`);
|
|
123
|
+
const entry = ICO_PALETTE[value];
|
|
124
|
+
if (entry === void 0) return void 0;
|
|
125
|
+
const [r, g, b] = entry;
|
|
126
|
+
return {
|
|
127
|
+
r: r / COLOR_COMPONENT_MAX,
|
|
128
|
+
g: g / COLOR_COMPONENT_MAX,
|
|
129
|
+
b: b / COLOR_COMPONENT_MAX
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function colorRefColor(operand) {
|
|
133
|
+
if (readUint8(operand, 3) !== 0) return void 0;
|
|
134
|
+
return {
|
|
135
|
+
r: readUint8(operand, 0) / COLOR_COMPONENT_MAX,
|
|
136
|
+
g: readUint8(operand, 1) / COLOR_COMPONENT_MAX,
|
|
137
|
+
b: readUint8(operand, 2) / COLOR_COMPONENT_MAX
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function applyCharacterSprms(prls, into) {
|
|
141
|
+
for (const prl of prls) {
|
|
142
|
+
if (prl.sprm.sgc !== SGC.character) continue;
|
|
143
|
+
switch (prl.sprm.value) {
|
|
144
|
+
case SPRM_C_F_BOLD:
|
|
145
|
+
into.bold = toggle(prl.operand, into.bold);
|
|
146
|
+
break;
|
|
147
|
+
case SPRM_C_F_ITALIC:
|
|
148
|
+
into.italic = toggle(prl.operand, into.italic);
|
|
149
|
+
break;
|
|
150
|
+
case SPRM_C_F_STRIKE:
|
|
151
|
+
into.strike = toggle(prl.operand, into.strike);
|
|
152
|
+
break;
|
|
153
|
+
case SPRM_C_KUL:
|
|
154
|
+
into.underline = readUint8(prl.operand, 0) !== 0;
|
|
155
|
+
break;
|
|
156
|
+
case SPRM_C_HPS:
|
|
157
|
+
into.sizePt = readUint16LE(prl.operand, 0) / HALF_POINTS_PER_POINT;
|
|
158
|
+
break;
|
|
159
|
+
case SPRM_C_ISTD:
|
|
160
|
+
into.istd = readUint16LE(prl.operand, 0);
|
|
161
|
+
break;
|
|
162
|
+
case SPRM_C_ICO:
|
|
163
|
+
into.color = icoColor(readUint8(prl.operand, 0));
|
|
164
|
+
break;
|
|
165
|
+
case SPRM_C_CV: into.color = colorRefColor(prl.operand);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return into;
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
export { applyCharacterSprms };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_errors = require("../errors.cjs");
|
|
3
|
+
const require_bytes = require("../bytes.cjs");
|
|
4
|
+
const require_plc = require("../plc.cjs");
|
|
5
|
+
//#region src/prop/fkp.ts
|
|
6
|
+
/** Every FKP is exactly one 512-byte page, whatever the compound file's own sector size. */
|
|
7
|
+
const FKP_PAGE_SIZE = 512;
|
|
8
|
+
/** ChpxFkp.crun "MUST be at least 0x01, and MUST NOT exceed 0x65". */
|
|
9
|
+
const MAX_CRUN = 101;
|
|
10
|
+
/** PapxFkp.cpara "MUST be at least 0x01, and MUST NOT exceed 0x1D". */
|
|
11
|
+
const MAX_CPARA = 29;
|
|
12
|
+
/** BxPap is a 1-byte bOffset followed by a 12-byte PHE the specification says SHOULD be zero and SHOULD be ignored. */
|
|
13
|
+
const BX_PAP_SIZE = 13;
|
|
14
|
+
/** PnFkpChpx/PnFkpPapx: "pn (22 bits)" then 10 unused, and "the ChpxFkp structure begins at an offset of pn x 512". */
|
|
15
|
+
const PN_MASK = 4194303;
|
|
16
|
+
function checkPage(page, what) {
|
|
17
|
+
if (page.length !== 512) throw new require_errors.DocFormatError(`${what} is ${page.length} bytes rather than the fixed 512 every formatted disk page occupies`);
|
|
18
|
+
}
|
|
19
|
+
function readRgfc(page, count) {
|
|
20
|
+
const rgfc = [];
|
|
21
|
+
for (let index = 0; index <= count; index += 1) rgfc.push(require_bytes.readUint32LE(page, index * 4));
|
|
22
|
+
return rgfc;
|
|
23
|
+
}
|
|
24
|
+
function parseChpxFkp(page) {
|
|
25
|
+
checkPage(page, "ChpxFkp");
|
|
26
|
+
const crun = require_bytes.readUint8(page, 511);
|
|
27
|
+
if (crun < 1 || crun > MAX_CRUN) throw new require_errors.DocFormatError(`ChpxFkp declares crun ${crun}, outside the 0x01..0x${MAX_CRUN.toString(16).toUpperCase()} range [MS-DOC] permits`);
|
|
28
|
+
const rgfc = readRgfc(page, crun);
|
|
29
|
+
const rgbStart = (crun + 1) * 4;
|
|
30
|
+
return {
|
|
31
|
+
rgfc,
|
|
32
|
+
grpprl(index) {
|
|
33
|
+
if (!Number.isInteger(index) || index < 0 || index >= crun) throw new require_errors.DocFormatError(`ChpxFkp covers ${crun} runs; run ${index} was requested`);
|
|
34
|
+
const halfOffset = require_bytes.readUint8(page, rgbStart + index);
|
|
35
|
+
if (halfOffset === 0) return void 0;
|
|
36
|
+
const chpxAt = halfOffset * 2;
|
|
37
|
+
const cb = require_bytes.readUint8(page, chpxAt);
|
|
38
|
+
return require_bytes.slice(page, chpxAt + 1, cb, `Chpx for ChpxFkp run ${index}`);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function parsePapxFkp(page) {
|
|
43
|
+
checkPage(page, "PapxFkp");
|
|
44
|
+
const cpara = require_bytes.readUint8(page, 511);
|
|
45
|
+
if (cpara < 1 || cpara > MAX_CPARA) throw new require_errors.DocFormatError(`PapxFkp declares cpara ${cpara}, outside the 0x01..0x${MAX_CPARA.toString(16).toUpperCase()} range [MS-DOC] permits`);
|
|
46
|
+
const rgfc = readRgfc(page, cpara);
|
|
47
|
+
const rgbxStart = (cpara + 1) * 4;
|
|
48
|
+
return {
|
|
49
|
+
rgfc,
|
|
50
|
+
papx(index) {
|
|
51
|
+
if (!Number.isInteger(index) || index < 0 || index >= cpara) throw new require_errors.DocFormatError(`PapxFkp covers ${cpara} paragraphs; paragraph ${index} was requested`);
|
|
52
|
+
const bOffset = require_bytes.readUint8(page, rgbxStart + index * BX_PAP_SIZE);
|
|
53
|
+
if (bOffset === 0) return void 0;
|
|
54
|
+
const papxAt = bOffset * 2;
|
|
55
|
+
const cb = require_bytes.readUint8(page, papxAt);
|
|
56
|
+
let grpPrlAndIstdAt;
|
|
57
|
+
let grpPrlAndIstdSize;
|
|
58
|
+
if (cb !== 0) {
|
|
59
|
+
grpPrlAndIstdAt = papxAt + 1;
|
|
60
|
+
grpPrlAndIstdSize = 2 * cb - 1;
|
|
61
|
+
} else {
|
|
62
|
+
const cbPrime = require_bytes.readUint8(page, papxAt + 1);
|
|
63
|
+
if (cbPrime < 1) throw new require_errors.DocFormatError(`PapxInFkp for paragraph ${index} has cb 0 and cb' 0, but [MS-DOC] requires cb' to be at least 1`);
|
|
64
|
+
grpPrlAndIstdAt = papxAt + 2;
|
|
65
|
+
grpPrlAndIstdSize = 2 * cbPrime;
|
|
66
|
+
}
|
|
67
|
+
if (grpPrlAndIstdSize < 2) throw new require_errors.DocFormatError(`PapxInFkp for paragraph ${index} declares a ${grpPrlAndIstdSize}-byte GrpPrlAndIstd, too short for the 2-byte istd it must begin with`);
|
|
68
|
+
return {
|
|
69
|
+
istd: require_bytes.readUint16LE(page, grpPrlAndIstdAt),
|
|
70
|
+
grpprl: require_bytes.slice(page, grpPrlAndIstdAt + 2, grpPrlAndIstdSize - 2, `grpprl of PapxInFkp for paragraph ${index}`)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
var PropertyBinTable = class {
|
|
76
|
+
#wordDocument;
|
|
77
|
+
#keys;
|
|
78
|
+
#pageNumbers;
|
|
79
|
+
#chpxPages = /* @__PURE__ */ new Map();
|
|
80
|
+
#papxPages = /* @__PURE__ */ new Map();
|
|
81
|
+
constructor(wordDocument, plc, what) {
|
|
82
|
+
this.#wordDocument = wordDocument;
|
|
83
|
+
const parsed = require_plc.parsePlc(plc, 4, what);
|
|
84
|
+
this.#keys = parsed.keys;
|
|
85
|
+
const pageNumbers = [];
|
|
86
|
+
for (let index = 0; index < parsed.count; index += 1) pageNumbers.push(require_bytes.readUint32LE(parsed.element(index), 0) & PN_MASK);
|
|
87
|
+
this.#pageNumbers = pageNumbers;
|
|
88
|
+
}
|
|
89
|
+
#pageBytes(fc) {
|
|
90
|
+
const index = require_plc.findLargestAtMost(this.#keys, fc);
|
|
91
|
+
if (index === void 0) return void 0;
|
|
92
|
+
const pageNumber = this.#pageNumbers[index];
|
|
93
|
+
if (pageNumber === void 0) throw new require_errors.DocFormatError(`bin table entry ${index} names no formatted-disk-page number`);
|
|
94
|
+
return require_bytes.slice(this.#wordDocument, pageNumber * 512, 512, `formatted disk page ${pageNumber}`);
|
|
95
|
+
}
|
|
96
|
+
#pageNumberFor(fc) {
|
|
97
|
+
const index = require_plc.findLargestAtMost(this.#keys, fc);
|
|
98
|
+
return index === void 0 ? void 0 : this.#pageNumbers[index];
|
|
99
|
+
}
|
|
100
|
+
/** The direct character-formatting grpprl covering `fc`, or undefined when the offset is outside the table or its run carries no exception. */
|
|
101
|
+
chpxGrpprl(fc) {
|
|
102
|
+
const pageNumber = this.#pageNumberFor(fc);
|
|
103
|
+
if (pageNumber === void 0) return void 0;
|
|
104
|
+
let fkp = this.#chpxPages.get(pageNumber);
|
|
105
|
+
if (fkp === void 0) {
|
|
106
|
+
const bytes = this.#pageBytes(fc);
|
|
107
|
+
if (bytes === void 0) return void 0;
|
|
108
|
+
fkp = parseChpxFkp(bytes);
|
|
109
|
+
this.#chpxPages.set(pageNumber, fkp);
|
|
110
|
+
}
|
|
111
|
+
const run = require_plc.findLargestAtMost(fkp.rgfc, fc);
|
|
112
|
+
return run === void 0 ? void 0 : fkp.grpprl(run);
|
|
113
|
+
}
|
|
114
|
+
/** The direct paragraph-formatting record covering `fc`, or undefined when the offset is outside the table or its paragraph carries no exception. */
|
|
115
|
+
papx(fc) {
|
|
116
|
+
const pageNumber = this.#pageNumberFor(fc);
|
|
117
|
+
if (pageNumber === void 0) return void 0;
|
|
118
|
+
let fkp = this.#papxPages.get(pageNumber);
|
|
119
|
+
if (fkp === void 0) {
|
|
120
|
+
const bytes = this.#pageBytes(fc);
|
|
121
|
+
if (bytes === void 0) return void 0;
|
|
122
|
+
fkp = parsePapxFkp(bytes);
|
|
123
|
+
this.#papxPages.set(pageNumber, fkp);
|
|
124
|
+
}
|
|
125
|
+
const index = require_plc.findLargestAtMost(fkp.rgfc, fc);
|
|
126
|
+
if (index === void 0) return void 0;
|
|
127
|
+
const record = fkp.papx(index);
|
|
128
|
+
if (record === void 0) return void 0;
|
|
129
|
+
const fcLim = fkp.rgfc[index + 1];
|
|
130
|
+
if (fcLim === void 0) throw new require_errors.DocFormatError(`PapxFkp paragraph ${index} has no bracketing end offset, so its extent is undefined`);
|
|
131
|
+
return {
|
|
132
|
+
...record,
|
|
133
|
+
fcLim
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
//#endregion
|
|
138
|
+
exports.FKP_PAGE_SIZE = FKP_PAGE_SIZE;
|
|
139
|
+
exports.PropertyBinTable = PropertyBinTable;
|
|
140
|
+
exports.parseChpxFkp = parseChpxFkp;
|
|
141
|
+
exports.parsePapxFkp = parsePapxFkp;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/prop/fkp.d.ts
|
|
2
|
+
/** Every FKP is exactly one 512-byte page, whatever the compound file's own sector size. */
|
|
3
|
+
declare const FKP_PAGE_SIZE = 512;
|
|
4
|
+
interface ChpxFkp {
|
|
5
|
+
readonly rgfc: readonly number[];
|
|
6
|
+
/** The Chpx's grpprl for run `index`, or undefined when the run's rgb entry is zero and it carries no exception. */
|
|
7
|
+
grpprl(index: number): Uint8Array | undefined;
|
|
8
|
+
}
|
|
9
|
+
interface PapxRecord {
|
|
10
|
+
readonly istd: number;
|
|
11
|
+
readonly grpprl: Uint8Array;
|
|
12
|
+
}
|
|
13
|
+
interface PapxFkp {
|
|
14
|
+
readonly rgfc: readonly number[];
|
|
15
|
+
/** The PapxInFkp for paragraph `index`, or undefined when its BxPap.bOffset is zero and the paragraph takes the defaults. */
|
|
16
|
+
papx(index: number): PapxRecord | undefined;
|
|
17
|
+
}
|
|
18
|
+
declare function parseChpxFkp(page: Uint8Array): ChpxFkp;
|
|
19
|
+
declare function parsePapxFkp(page: Uint8Array): PapxFkp;
|
|
20
|
+
interface PapxLookup extends PapxRecord {
|
|
21
|
+
/** The byte offset one past the end of the paragraph this record covers -- PapxFkp.rgfc[k + 1], the paragraph boundary [MS-DOC] 2.4.2 derives its own from. */
|
|
22
|
+
readonly fcLim: number;
|
|
23
|
+
}
|
|
24
|
+
declare class PropertyBinTable {
|
|
25
|
+
#private;
|
|
26
|
+
constructor(wordDocument: Uint8Array, plc: Uint8Array, what: string);
|
|
27
|
+
/** The direct character-formatting grpprl covering `fc`, or undefined when the offset is outside the table or its run carries no exception. */
|
|
28
|
+
chpxGrpprl(fc: number): Uint8Array | undefined;
|
|
29
|
+
/** The direct paragraph-formatting record covering `fc`, or undefined when the offset is outside the table or its paragraph carries no exception. */
|
|
30
|
+
papx(fc: number): PapxLookup | undefined;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { ChpxFkp, FKP_PAGE_SIZE, PapxFkp, PapxLookup, PapxRecord, PropertyBinTable, parseChpxFkp, parsePapxFkp };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/prop/fkp.d.ts
|
|
2
|
+
/** Every FKP is exactly one 512-byte page, whatever the compound file's own sector size. */
|
|
3
|
+
declare const FKP_PAGE_SIZE = 512;
|
|
4
|
+
interface ChpxFkp {
|
|
5
|
+
readonly rgfc: readonly number[];
|
|
6
|
+
/** The Chpx's grpprl for run `index`, or undefined when the run's rgb entry is zero and it carries no exception. */
|
|
7
|
+
grpprl(index: number): Uint8Array | undefined;
|
|
8
|
+
}
|
|
9
|
+
interface PapxRecord {
|
|
10
|
+
readonly istd: number;
|
|
11
|
+
readonly grpprl: Uint8Array;
|
|
12
|
+
}
|
|
13
|
+
interface PapxFkp {
|
|
14
|
+
readonly rgfc: readonly number[];
|
|
15
|
+
/** The PapxInFkp for paragraph `index`, or undefined when its BxPap.bOffset is zero and the paragraph takes the defaults. */
|
|
16
|
+
papx(index: number): PapxRecord | undefined;
|
|
17
|
+
}
|
|
18
|
+
declare function parseChpxFkp(page: Uint8Array): ChpxFkp;
|
|
19
|
+
declare function parsePapxFkp(page: Uint8Array): PapxFkp;
|
|
20
|
+
interface PapxLookup extends PapxRecord {
|
|
21
|
+
/** The byte offset one past the end of the paragraph this record covers -- PapxFkp.rgfc[k + 1], the paragraph boundary [MS-DOC] 2.4.2 derives its own from. */
|
|
22
|
+
readonly fcLim: number;
|
|
23
|
+
}
|
|
24
|
+
declare class PropertyBinTable {
|
|
25
|
+
#private;
|
|
26
|
+
constructor(wordDocument: Uint8Array, plc: Uint8Array, what: string);
|
|
27
|
+
/** The direct character-formatting grpprl covering `fc`, or undefined when the offset is outside the table or its run carries no exception. */
|
|
28
|
+
chpxGrpprl(fc: number): Uint8Array | undefined;
|
|
29
|
+
/** The direct paragraph-formatting record covering `fc`, or undefined when the offset is outside the table or its paragraph carries no exception. */
|
|
30
|
+
papx(fc: number): PapxLookup | undefined;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { ChpxFkp, FKP_PAGE_SIZE, PapxFkp, PapxLookup, PapxRecord, PropertyBinTable, parseChpxFkp, parsePapxFkp };
|
package/dist/prop/fkp.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { DocFormatError } from "../errors.js";
|
|
2
|
+
import { readUint16LE, readUint32LE, readUint8, slice } from "../bytes.js";
|
|
3
|
+
import { findLargestAtMost, parsePlc } from "../plc.js";
|
|
4
|
+
//#region src/prop/fkp.ts
|
|
5
|
+
/** Every FKP is exactly one 512-byte page, whatever the compound file's own sector size. */
|
|
6
|
+
const FKP_PAGE_SIZE = 512;
|
|
7
|
+
/** ChpxFkp.crun "MUST be at least 0x01, and MUST NOT exceed 0x65". */
|
|
8
|
+
const MAX_CRUN = 101;
|
|
9
|
+
/** PapxFkp.cpara "MUST be at least 0x01, and MUST NOT exceed 0x1D". */
|
|
10
|
+
const MAX_CPARA = 29;
|
|
11
|
+
/** BxPap is a 1-byte bOffset followed by a 12-byte PHE the specification says SHOULD be zero and SHOULD be ignored. */
|
|
12
|
+
const BX_PAP_SIZE = 13;
|
|
13
|
+
/** PnFkpChpx/PnFkpPapx: "pn (22 bits)" then 10 unused, and "the ChpxFkp structure begins at an offset of pn x 512". */
|
|
14
|
+
const PN_MASK = 4194303;
|
|
15
|
+
function checkPage(page, what) {
|
|
16
|
+
if (page.length !== 512) throw new DocFormatError(`${what} is ${page.length} bytes rather than the fixed 512 every formatted disk page occupies`);
|
|
17
|
+
}
|
|
18
|
+
function readRgfc(page, count) {
|
|
19
|
+
const rgfc = [];
|
|
20
|
+
for (let index = 0; index <= count; index += 1) rgfc.push(readUint32LE(page, index * 4));
|
|
21
|
+
return rgfc;
|
|
22
|
+
}
|
|
23
|
+
function parseChpxFkp(page) {
|
|
24
|
+
checkPage(page, "ChpxFkp");
|
|
25
|
+
const crun = readUint8(page, 511);
|
|
26
|
+
if (crun < 1 || crun > MAX_CRUN) throw new DocFormatError(`ChpxFkp declares crun ${crun}, outside the 0x01..0x${MAX_CRUN.toString(16).toUpperCase()} range [MS-DOC] permits`);
|
|
27
|
+
const rgfc = readRgfc(page, crun);
|
|
28
|
+
const rgbStart = (crun + 1) * 4;
|
|
29
|
+
return {
|
|
30
|
+
rgfc,
|
|
31
|
+
grpprl(index) {
|
|
32
|
+
if (!Number.isInteger(index) || index < 0 || index >= crun) throw new DocFormatError(`ChpxFkp covers ${crun} runs; run ${index} was requested`);
|
|
33
|
+
const halfOffset = readUint8(page, rgbStart + index);
|
|
34
|
+
if (halfOffset === 0) return void 0;
|
|
35
|
+
const chpxAt = halfOffset * 2;
|
|
36
|
+
const cb = readUint8(page, chpxAt);
|
|
37
|
+
return slice(page, chpxAt + 1, cb, `Chpx for ChpxFkp run ${index}`);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function parsePapxFkp(page) {
|
|
42
|
+
checkPage(page, "PapxFkp");
|
|
43
|
+
const cpara = readUint8(page, 511);
|
|
44
|
+
if (cpara < 1 || cpara > MAX_CPARA) throw new DocFormatError(`PapxFkp declares cpara ${cpara}, outside the 0x01..0x${MAX_CPARA.toString(16).toUpperCase()} range [MS-DOC] permits`);
|
|
45
|
+
const rgfc = readRgfc(page, cpara);
|
|
46
|
+
const rgbxStart = (cpara + 1) * 4;
|
|
47
|
+
return {
|
|
48
|
+
rgfc,
|
|
49
|
+
papx(index) {
|
|
50
|
+
if (!Number.isInteger(index) || index < 0 || index >= cpara) throw new DocFormatError(`PapxFkp covers ${cpara} paragraphs; paragraph ${index} was requested`);
|
|
51
|
+
const bOffset = readUint8(page, rgbxStart + index * BX_PAP_SIZE);
|
|
52
|
+
if (bOffset === 0) return void 0;
|
|
53
|
+
const papxAt = bOffset * 2;
|
|
54
|
+
const cb = readUint8(page, papxAt);
|
|
55
|
+
let grpPrlAndIstdAt;
|
|
56
|
+
let grpPrlAndIstdSize;
|
|
57
|
+
if (cb !== 0) {
|
|
58
|
+
grpPrlAndIstdAt = papxAt + 1;
|
|
59
|
+
grpPrlAndIstdSize = 2 * cb - 1;
|
|
60
|
+
} else {
|
|
61
|
+
const cbPrime = readUint8(page, papxAt + 1);
|
|
62
|
+
if (cbPrime < 1) throw new DocFormatError(`PapxInFkp for paragraph ${index} has cb 0 and cb' 0, but [MS-DOC] requires cb' to be at least 1`);
|
|
63
|
+
grpPrlAndIstdAt = papxAt + 2;
|
|
64
|
+
grpPrlAndIstdSize = 2 * cbPrime;
|
|
65
|
+
}
|
|
66
|
+
if (grpPrlAndIstdSize < 2) throw new DocFormatError(`PapxInFkp for paragraph ${index} declares a ${grpPrlAndIstdSize}-byte GrpPrlAndIstd, too short for the 2-byte istd it must begin with`);
|
|
67
|
+
return {
|
|
68
|
+
istd: readUint16LE(page, grpPrlAndIstdAt),
|
|
69
|
+
grpprl: slice(page, grpPrlAndIstdAt + 2, grpPrlAndIstdSize - 2, `grpprl of PapxInFkp for paragraph ${index}`)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
var PropertyBinTable = class {
|
|
75
|
+
#wordDocument;
|
|
76
|
+
#keys;
|
|
77
|
+
#pageNumbers;
|
|
78
|
+
#chpxPages = /* @__PURE__ */ new Map();
|
|
79
|
+
#papxPages = /* @__PURE__ */ new Map();
|
|
80
|
+
constructor(wordDocument, plc, what) {
|
|
81
|
+
this.#wordDocument = wordDocument;
|
|
82
|
+
const parsed = parsePlc(plc, 4, what);
|
|
83
|
+
this.#keys = parsed.keys;
|
|
84
|
+
const pageNumbers = [];
|
|
85
|
+
for (let index = 0; index < parsed.count; index += 1) pageNumbers.push(readUint32LE(parsed.element(index), 0) & PN_MASK);
|
|
86
|
+
this.#pageNumbers = pageNumbers;
|
|
87
|
+
}
|
|
88
|
+
#pageBytes(fc) {
|
|
89
|
+
const index = findLargestAtMost(this.#keys, fc);
|
|
90
|
+
if (index === void 0) return void 0;
|
|
91
|
+
const pageNumber = this.#pageNumbers[index];
|
|
92
|
+
if (pageNumber === void 0) throw new DocFormatError(`bin table entry ${index} names no formatted-disk-page number`);
|
|
93
|
+
return slice(this.#wordDocument, pageNumber * 512, 512, `formatted disk page ${pageNumber}`);
|
|
94
|
+
}
|
|
95
|
+
#pageNumberFor(fc) {
|
|
96
|
+
const index = findLargestAtMost(this.#keys, fc);
|
|
97
|
+
return index === void 0 ? void 0 : this.#pageNumbers[index];
|
|
98
|
+
}
|
|
99
|
+
/** The direct character-formatting grpprl covering `fc`, or undefined when the offset is outside the table or its run carries no exception. */
|
|
100
|
+
chpxGrpprl(fc) {
|
|
101
|
+
const pageNumber = this.#pageNumberFor(fc);
|
|
102
|
+
if (pageNumber === void 0) return void 0;
|
|
103
|
+
let fkp = this.#chpxPages.get(pageNumber);
|
|
104
|
+
if (fkp === void 0) {
|
|
105
|
+
const bytes = this.#pageBytes(fc);
|
|
106
|
+
if (bytes === void 0) return void 0;
|
|
107
|
+
fkp = parseChpxFkp(bytes);
|
|
108
|
+
this.#chpxPages.set(pageNumber, fkp);
|
|
109
|
+
}
|
|
110
|
+
const run = findLargestAtMost(fkp.rgfc, fc);
|
|
111
|
+
return run === void 0 ? void 0 : fkp.grpprl(run);
|
|
112
|
+
}
|
|
113
|
+
/** The direct paragraph-formatting record covering `fc`, or undefined when the offset is outside the table or its paragraph carries no exception. */
|
|
114
|
+
papx(fc) {
|
|
115
|
+
const pageNumber = this.#pageNumberFor(fc);
|
|
116
|
+
if (pageNumber === void 0) return void 0;
|
|
117
|
+
let fkp = this.#papxPages.get(pageNumber);
|
|
118
|
+
if (fkp === void 0) {
|
|
119
|
+
const bytes = this.#pageBytes(fc);
|
|
120
|
+
if (bytes === void 0) return void 0;
|
|
121
|
+
fkp = parsePapxFkp(bytes);
|
|
122
|
+
this.#papxPages.set(pageNumber, fkp);
|
|
123
|
+
}
|
|
124
|
+
const index = findLargestAtMost(fkp.rgfc, fc);
|
|
125
|
+
if (index === void 0) return void 0;
|
|
126
|
+
const record = fkp.papx(index);
|
|
127
|
+
if (record === void 0) return void 0;
|
|
128
|
+
const fcLim = fkp.rgfc[index + 1];
|
|
129
|
+
if (fcLim === void 0) throw new DocFormatError(`PapxFkp paragraph ${index} has no bracketing end offset, so its extent is undefined`);
|
|
130
|
+
return {
|
|
131
|
+
...record,
|
|
132
|
+
fcLim
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
//#endregion
|
|
137
|
+
export { FKP_PAGE_SIZE, PropertyBinTable, parseChpxFkp, parsePapxFkp };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_bytes = require("../bytes.cjs");
|
|
3
|
+
const require_prop_sprm = require("./sprm.cjs");
|
|
4
|
+
//#region src/prop/pap.ts
|
|
5
|
+
/** sprmPIstd: the istd of the paragraph style. */
|
|
6
|
+
const SPRM_P_ISTD = 17920;
|
|
7
|
+
/** sprmPJc80: physical justification, six values. */
|
|
8
|
+
const SPRM_P_JC_80 = 9219;
|
|
9
|
+
/** sprmPJc: logical justification, ten values aligned with ECMA-376's ST_Jc. */
|
|
10
|
+
const SPRM_P_JC = 9313;
|
|
11
|
+
/** sprmPFPageBreakBefore: a Bool8. */
|
|
12
|
+
const SPRM_P_F_PAGE_BREAK_BEFORE = 9223;
|
|
13
|
+
/** sprmPDxaLeft80 / sprmPDxaLeft: the left indent in twips. */
|
|
14
|
+
const SPRM_P_DXA_LEFT_80 = 33807;
|
|
15
|
+
const SPRM_P_DXA_LEFT = 33886;
|
|
16
|
+
/** sprmPDxaRight80 / sprmPDxaRight: the right indent in twips. */
|
|
17
|
+
const SPRM_P_DXA_RIGHT_80 = 33806;
|
|
18
|
+
const SPRM_P_DXA_RIGHT = 33885;
|
|
19
|
+
/** sprmPDxaLeft180 / sprmPDxaLeft1: the first line's indent relative to the rest of the paragraph. */
|
|
20
|
+
const SPRM_P_DXA_LEFT1_80 = 33809;
|
|
21
|
+
const SPRM_P_DXA_LEFT1 = 33888;
|
|
22
|
+
/** sprmPDyaBefore / sprmPDyaAfter: spacing in twips. */
|
|
23
|
+
const SPRM_P_DYA_BEFORE = 42003;
|
|
24
|
+
const SPRM_P_DYA_AFTER = 42004;
|
|
25
|
+
/** sprmPDyaLine: an LSPD. */
|
|
26
|
+
const SPRM_P_DYA_LINE = 25618;
|
|
27
|
+
/** sprmPOutLvl: the zero-based outline level, or 0x9 for body text. */
|
|
28
|
+
const SPRM_P_OUT_LVL = 9792;
|
|
29
|
+
/** sprmPFInTable: a Bool8 that "MUST be 1 any time the table depth is greater than zero". */
|
|
30
|
+
const SPRM_P_F_IN_TABLE = 9238;
|
|
31
|
+
/** sprmPFTtp: marks a cell mark as a table-terminating-paragraph mark, i.e. the end of a row. */
|
|
32
|
+
const SPRM_P_F_TTP = 9239;
|
|
33
|
+
/** sprmPIlvl: the zero-based list level, or 0xC for a paragraph the list skips. */
|
|
34
|
+
const SPRM_P_ILVL = 9738;
|
|
35
|
+
/** sprmPIlfo: which list the paragraph is in, as an index into PlfLfo.rgLfo. */
|
|
36
|
+
const SPRM_P_ILFO = 17931;
|
|
37
|
+
const TWIPS_PER_POINT = 20;
|
|
38
|
+
/** LSPD: "The spacing multiplier is dyaLine/240." */
|
|
39
|
+
const LSPD_MULTIPLE_DIVISOR = 240;
|
|
40
|
+
/** LSPD.dyaLine's multiplier form applies only "when dyaLine is between 0x0000 and 0x7BC0". */
|
|
41
|
+
const LSPD_MAX_MULTIPLE_DYA_LINE = 31680;
|
|
42
|
+
/** sprmPOutLvl's "0x9 - The paragraph at any outline level; instead, the paragraph is body text." */
|
|
43
|
+
const OUT_LVL_BODY_TEXT = 9;
|
|
44
|
+
/** sprmPIlvl's "0xC - The list skips this paragraph and does not include it in its numbering." */
|
|
45
|
+
const ILVL_SKIPPED = 12;
|
|
46
|
+
/** sprmPIlfo's two "not in a list" values: "0x0000 - This paragraph is not in a list" and "0xF801 - This paragraph is not in a list." */
|
|
47
|
+
const ILFO_NOT_IN_LIST = 0;
|
|
48
|
+
const ILFO_NOT_IN_LIST_ALT = -2047;
|
|
49
|
+
function twipsToPoints(twips) {
|
|
50
|
+
return twips / TWIPS_PER_POINT;
|
|
51
|
+
}
|
|
52
|
+
function alignmentFromJc(value) {
|
|
53
|
+
switch (value) {
|
|
54
|
+
case 0: return "left";
|
|
55
|
+
case 1: return "center";
|
|
56
|
+
case 2: return "right";
|
|
57
|
+
case 3:
|
|
58
|
+
case 4:
|
|
59
|
+
case 5:
|
|
60
|
+
case 7:
|
|
61
|
+
case 8:
|
|
62
|
+
case 9: return "justify";
|
|
63
|
+
default: return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function lineSpacingFromLspd(operand) {
|
|
67
|
+
const dyaLine = require_bytes.readInt16LE(operand, 0);
|
|
68
|
+
if (require_bytes.readUint16LE(operand, 2) !== 1) return void 0;
|
|
69
|
+
if (dyaLine < 0 || dyaLine > LSPD_MAX_MULTIPLE_DYA_LINE) return void 0;
|
|
70
|
+
const multiple = dyaLine / LSPD_MULTIPLE_DIVISOR;
|
|
71
|
+
return multiple > 0 ? multiple : void 0;
|
|
72
|
+
}
|
|
73
|
+
function applyParagraphSprms(prls, into) {
|
|
74
|
+
for (const prl of prls) {
|
|
75
|
+
if (prl.sprm.sgc !== require_prop_sprm.SGC.paragraph) continue;
|
|
76
|
+
switch (prl.sprm.value) {
|
|
77
|
+
case SPRM_P_ISTD:
|
|
78
|
+
into.istd = require_bytes.readUint16LE(prl.operand, 0);
|
|
79
|
+
break;
|
|
80
|
+
case SPRM_P_JC_80:
|
|
81
|
+
case SPRM_P_JC:
|
|
82
|
+
into.alignment = alignmentFromJc(require_bytes.readUint8(prl.operand, 0));
|
|
83
|
+
break;
|
|
84
|
+
case SPRM_P_F_PAGE_BREAK_BEFORE:
|
|
85
|
+
into.pageBreakBefore = require_bytes.readUint8(prl.operand, 0) !== 0;
|
|
86
|
+
break;
|
|
87
|
+
case SPRM_P_DXA_LEFT_80:
|
|
88
|
+
case SPRM_P_DXA_LEFT:
|
|
89
|
+
into.indentLeftPt = twipsToPoints(require_bytes.readInt16LE(prl.operand, 0));
|
|
90
|
+
break;
|
|
91
|
+
case SPRM_P_DXA_RIGHT_80:
|
|
92
|
+
case SPRM_P_DXA_RIGHT:
|
|
93
|
+
into.indentRightPt = twipsToPoints(require_bytes.readInt16LE(prl.operand, 0));
|
|
94
|
+
break;
|
|
95
|
+
case SPRM_P_DXA_LEFT1_80:
|
|
96
|
+
case SPRM_P_DXA_LEFT1:
|
|
97
|
+
into.indentFirstLinePt = twipsToPoints(require_bytes.readInt16LE(prl.operand, 0));
|
|
98
|
+
break;
|
|
99
|
+
case SPRM_P_DYA_BEFORE:
|
|
100
|
+
into.spacingBeforePt = twipsToPoints(require_bytes.readUint16LE(prl.operand, 0));
|
|
101
|
+
break;
|
|
102
|
+
case SPRM_P_DYA_AFTER:
|
|
103
|
+
into.spacingAfterPt = twipsToPoints(require_bytes.readUint16LE(prl.operand, 0));
|
|
104
|
+
break;
|
|
105
|
+
case SPRM_P_DYA_LINE:
|
|
106
|
+
into.lineSpacing = lineSpacingFromLspd(prl.operand);
|
|
107
|
+
break;
|
|
108
|
+
case SPRM_P_OUT_LVL: {
|
|
109
|
+
const level = require_bytes.readUint8(prl.operand, 0);
|
|
110
|
+
into.outlineLevel = level === OUT_LVL_BODY_TEXT ? void 0 : level;
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case SPRM_P_F_IN_TABLE:
|
|
114
|
+
into.inTable = require_bytes.readUint8(prl.operand, 0) !== 0;
|
|
115
|
+
break;
|
|
116
|
+
case SPRM_P_F_TTP:
|
|
117
|
+
into.tableRowEnd = require_bytes.readUint8(prl.operand, 0) !== 0;
|
|
118
|
+
break;
|
|
119
|
+
case SPRM_P_ILVL: {
|
|
120
|
+
const level = require_bytes.readUint8(prl.operand, 0);
|
|
121
|
+
into.listLevel = level === ILVL_SKIPPED ? void 0 : level;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case SPRM_P_ILFO: {
|
|
125
|
+
const ilfo = require_bytes.readInt16LE(prl.operand, 0);
|
|
126
|
+
into.listId = ilfo === ILFO_NOT_IN_LIST || ilfo === ILFO_NOT_IN_LIST_ALT ? void 0 : Math.abs(ilfo);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return into;
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
exports.applyParagraphSprms = applyParagraphSprms;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Prl } from "./sprm.cjs";
|
|
2
|
+
import { Alignment } from "document-schema.js";
|
|
3
|
+
//#region src/prop/pap.d.ts
|
|
4
|
+
interface ParagraphProperties {
|
|
5
|
+
istd?: number;
|
|
6
|
+
alignment?: Alignment;
|
|
7
|
+
indentLeftPt?: number;
|
|
8
|
+
indentRightPt?: number;
|
|
9
|
+
indentFirstLinePt?: number;
|
|
10
|
+
spacingBeforePt?: number;
|
|
11
|
+
spacingAfterPt?: number;
|
|
12
|
+
lineSpacing?: number;
|
|
13
|
+
pageBreakBefore?: boolean;
|
|
14
|
+
/** sprmPOutLvl's zero-based level, present only when the paragraph is genuinely in an outline level rather than body text. */
|
|
15
|
+
outlineLevel?: number;
|
|
16
|
+
inTable?: boolean;
|
|
17
|
+
/** True on the cell mark that terminates a table row. */
|
|
18
|
+
tableRowEnd?: boolean;
|
|
19
|
+
listLevel?: number;
|
|
20
|
+
/** The list identifier (sprmPIlfo), present only when the paragraph is in a list at all. */
|
|
21
|
+
listId?: number;
|
|
22
|
+
}
|
|
23
|
+
declare function applyParagraphSprms(prls: readonly Prl[], into: ParagraphProperties): ParagraphProperties;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { ParagraphProperties, applyParagraphSprms };
|