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
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Prl } from "./sprm.js";
|
|
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 };
|
package/dist/prop/pap.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { readInt16LE, readUint16LE, readUint8 } from "../bytes.js";
|
|
2
|
+
import { SGC } from "./sprm.js";
|
|
3
|
+
//#region src/prop/pap.ts
|
|
4
|
+
/** sprmPIstd: the istd of the paragraph style. */
|
|
5
|
+
const SPRM_P_ISTD = 17920;
|
|
6
|
+
/** sprmPJc80: physical justification, six values. */
|
|
7
|
+
const SPRM_P_JC_80 = 9219;
|
|
8
|
+
/** sprmPJc: logical justification, ten values aligned with ECMA-376's ST_Jc. */
|
|
9
|
+
const SPRM_P_JC = 9313;
|
|
10
|
+
/** sprmPFPageBreakBefore: a Bool8. */
|
|
11
|
+
const SPRM_P_F_PAGE_BREAK_BEFORE = 9223;
|
|
12
|
+
/** sprmPDxaLeft80 / sprmPDxaLeft: the left indent in twips. */
|
|
13
|
+
const SPRM_P_DXA_LEFT_80 = 33807;
|
|
14
|
+
const SPRM_P_DXA_LEFT = 33886;
|
|
15
|
+
/** sprmPDxaRight80 / sprmPDxaRight: the right indent in twips. */
|
|
16
|
+
const SPRM_P_DXA_RIGHT_80 = 33806;
|
|
17
|
+
const SPRM_P_DXA_RIGHT = 33885;
|
|
18
|
+
/** sprmPDxaLeft180 / sprmPDxaLeft1: the first line's indent relative to the rest of the paragraph. */
|
|
19
|
+
const SPRM_P_DXA_LEFT1_80 = 33809;
|
|
20
|
+
const SPRM_P_DXA_LEFT1 = 33888;
|
|
21
|
+
/** sprmPDyaBefore / sprmPDyaAfter: spacing in twips. */
|
|
22
|
+
const SPRM_P_DYA_BEFORE = 42003;
|
|
23
|
+
const SPRM_P_DYA_AFTER = 42004;
|
|
24
|
+
/** sprmPDyaLine: an LSPD. */
|
|
25
|
+
const SPRM_P_DYA_LINE = 25618;
|
|
26
|
+
/** sprmPOutLvl: the zero-based outline level, or 0x9 for body text. */
|
|
27
|
+
const SPRM_P_OUT_LVL = 9792;
|
|
28
|
+
/** sprmPFInTable: a Bool8 that "MUST be 1 any time the table depth is greater than zero". */
|
|
29
|
+
const SPRM_P_F_IN_TABLE = 9238;
|
|
30
|
+
/** sprmPFTtp: marks a cell mark as a table-terminating-paragraph mark, i.e. the end of a row. */
|
|
31
|
+
const SPRM_P_F_TTP = 9239;
|
|
32
|
+
/** sprmPIlvl: the zero-based list level, or 0xC for a paragraph the list skips. */
|
|
33
|
+
const SPRM_P_ILVL = 9738;
|
|
34
|
+
/** sprmPIlfo: which list the paragraph is in, as an index into PlfLfo.rgLfo. */
|
|
35
|
+
const SPRM_P_ILFO = 17931;
|
|
36
|
+
const TWIPS_PER_POINT = 20;
|
|
37
|
+
/** LSPD: "The spacing multiplier is dyaLine/240." */
|
|
38
|
+
const LSPD_MULTIPLE_DIVISOR = 240;
|
|
39
|
+
/** LSPD.dyaLine's multiplier form applies only "when dyaLine is between 0x0000 and 0x7BC0". */
|
|
40
|
+
const LSPD_MAX_MULTIPLE_DYA_LINE = 31680;
|
|
41
|
+
/** sprmPOutLvl's "0x9 - The paragraph at any outline level; instead, the paragraph is body text." */
|
|
42
|
+
const OUT_LVL_BODY_TEXT = 9;
|
|
43
|
+
/** sprmPIlvl's "0xC - The list skips this paragraph and does not include it in its numbering." */
|
|
44
|
+
const ILVL_SKIPPED = 12;
|
|
45
|
+
/** 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." */
|
|
46
|
+
const ILFO_NOT_IN_LIST = 0;
|
|
47
|
+
const ILFO_NOT_IN_LIST_ALT = -2047;
|
|
48
|
+
function twipsToPoints(twips) {
|
|
49
|
+
return twips / TWIPS_PER_POINT;
|
|
50
|
+
}
|
|
51
|
+
function alignmentFromJc(value) {
|
|
52
|
+
switch (value) {
|
|
53
|
+
case 0: return "left";
|
|
54
|
+
case 1: return "center";
|
|
55
|
+
case 2: return "right";
|
|
56
|
+
case 3:
|
|
57
|
+
case 4:
|
|
58
|
+
case 5:
|
|
59
|
+
case 7:
|
|
60
|
+
case 8:
|
|
61
|
+
case 9: return "justify";
|
|
62
|
+
default: return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function lineSpacingFromLspd(operand) {
|
|
66
|
+
const dyaLine = readInt16LE(operand, 0);
|
|
67
|
+
if (readUint16LE(operand, 2) !== 1) return void 0;
|
|
68
|
+
if (dyaLine < 0 || dyaLine > LSPD_MAX_MULTIPLE_DYA_LINE) return void 0;
|
|
69
|
+
const multiple = dyaLine / LSPD_MULTIPLE_DIVISOR;
|
|
70
|
+
return multiple > 0 ? multiple : void 0;
|
|
71
|
+
}
|
|
72
|
+
function applyParagraphSprms(prls, into) {
|
|
73
|
+
for (const prl of prls) {
|
|
74
|
+
if (prl.sprm.sgc !== SGC.paragraph) continue;
|
|
75
|
+
switch (prl.sprm.value) {
|
|
76
|
+
case SPRM_P_ISTD:
|
|
77
|
+
into.istd = readUint16LE(prl.operand, 0);
|
|
78
|
+
break;
|
|
79
|
+
case SPRM_P_JC_80:
|
|
80
|
+
case SPRM_P_JC:
|
|
81
|
+
into.alignment = alignmentFromJc(readUint8(prl.operand, 0));
|
|
82
|
+
break;
|
|
83
|
+
case SPRM_P_F_PAGE_BREAK_BEFORE:
|
|
84
|
+
into.pageBreakBefore = readUint8(prl.operand, 0) !== 0;
|
|
85
|
+
break;
|
|
86
|
+
case SPRM_P_DXA_LEFT_80:
|
|
87
|
+
case SPRM_P_DXA_LEFT:
|
|
88
|
+
into.indentLeftPt = twipsToPoints(readInt16LE(prl.operand, 0));
|
|
89
|
+
break;
|
|
90
|
+
case SPRM_P_DXA_RIGHT_80:
|
|
91
|
+
case SPRM_P_DXA_RIGHT:
|
|
92
|
+
into.indentRightPt = twipsToPoints(readInt16LE(prl.operand, 0));
|
|
93
|
+
break;
|
|
94
|
+
case SPRM_P_DXA_LEFT1_80:
|
|
95
|
+
case SPRM_P_DXA_LEFT1:
|
|
96
|
+
into.indentFirstLinePt = twipsToPoints(readInt16LE(prl.operand, 0));
|
|
97
|
+
break;
|
|
98
|
+
case SPRM_P_DYA_BEFORE:
|
|
99
|
+
into.spacingBeforePt = twipsToPoints(readUint16LE(prl.operand, 0));
|
|
100
|
+
break;
|
|
101
|
+
case SPRM_P_DYA_AFTER:
|
|
102
|
+
into.spacingAfterPt = twipsToPoints(readUint16LE(prl.operand, 0));
|
|
103
|
+
break;
|
|
104
|
+
case SPRM_P_DYA_LINE:
|
|
105
|
+
into.lineSpacing = lineSpacingFromLspd(prl.operand);
|
|
106
|
+
break;
|
|
107
|
+
case SPRM_P_OUT_LVL: {
|
|
108
|
+
const level = readUint8(prl.operand, 0);
|
|
109
|
+
into.outlineLevel = level === OUT_LVL_BODY_TEXT ? void 0 : level;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case SPRM_P_F_IN_TABLE:
|
|
113
|
+
into.inTable = readUint8(prl.operand, 0) !== 0;
|
|
114
|
+
break;
|
|
115
|
+
case SPRM_P_F_TTP:
|
|
116
|
+
into.tableRowEnd = readUint8(prl.operand, 0) !== 0;
|
|
117
|
+
break;
|
|
118
|
+
case SPRM_P_ILVL: {
|
|
119
|
+
const level = readUint8(prl.operand, 0);
|
|
120
|
+
into.listLevel = level === ILVL_SKIPPED ? void 0 : level;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case SPRM_P_ILFO: {
|
|
124
|
+
const ilfo = readInt16LE(prl.operand, 0);
|
|
125
|
+
into.listId = ilfo === ILFO_NOT_IN_LIST || ilfo === ILFO_NOT_IN_LIST_ALT ? void 0 : Math.abs(ilfo);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return into;
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { applyParagraphSprms };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_errors = require("../errors.cjs");
|
|
3
|
+
const require_bytes = require("../bytes.cjs");
|
|
4
|
+
//#region src/prop/sprm.ts
|
|
5
|
+
/** Sprm.sgc, [MS-DOC] 2.6.1 -- which property family the opcode belongs to. */
|
|
6
|
+
const SGC = {
|
|
7
|
+
paragraph: 1,
|
|
8
|
+
character: 2,
|
|
9
|
+
picture: 3,
|
|
10
|
+
section: 4,
|
|
11
|
+
table: 5
|
|
12
|
+
};
|
|
13
|
+
const SPRM_T_DEF_TABLE = 54792;
|
|
14
|
+
const SPRM_P_CHG_TABS = 50709;
|
|
15
|
+
/** PChgTabsOperand.cb: "A value of 255 specifies that this instance of sprmPChgTabs MAY be ignored and that the size of the remainder of this operand ... is calculated by using the following formula". */
|
|
16
|
+
const P_CHG_TABS_COMPUTED_SIZE = 255;
|
|
17
|
+
function decodeSprm(value) {
|
|
18
|
+
return {
|
|
19
|
+
value,
|
|
20
|
+
ispmd: value & 511,
|
|
21
|
+
fSpec: (value >> 9 & 1) === 1 ? 1 : 0,
|
|
22
|
+
sgc: value >> 10 & 7,
|
|
23
|
+
spra: value >> 13 & 7
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function operandSize(sprm, bytes, offset) {
|
|
27
|
+
switch (sprm.spra) {
|
|
28
|
+
case 0:
|
|
29
|
+
case 1: return 1;
|
|
30
|
+
case 2:
|
|
31
|
+
case 4:
|
|
32
|
+
case 5: return 2;
|
|
33
|
+
case 3: return 4;
|
|
34
|
+
case 7: return 3;
|
|
35
|
+
case 6: return variableOperandSize(sprm, bytes, offset);
|
|
36
|
+
default: throw new require_errors.DocFormatError(`sprm 0x${sprm.value.toString(16)} has spra ${sprm.spra}, which is outside the 0..7 range a 3-bit field can hold`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function variableOperandSize(sprm, bytes, offset) {
|
|
40
|
+
if (sprm.value === SPRM_T_DEF_TABLE) {
|
|
41
|
+
const cb = require_bytes.readUint16LE(bytes, offset);
|
|
42
|
+
if (cb < 1) throw new require_errors.DocFormatError(`sprmTDefTable declares cb ${cb}, which cannot be a remainder length incremented by 1`);
|
|
43
|
+
return cb + 1;
|
|
44
|
+
}
|
|
45
|
+
const cb = require_bytes.readUint8(bytes, offset);
|
|
46
|
+
if (sprm.value === SPRM_P_CHG_TABS && cb === P_CHG_TABS_COMPUTED_SIZE) throw new require_errors.DocUnsupportedError("this paragraph carries a sprmPChgTabs whose cb is the 255 sentinel, so its operand length is computed from tab-stop counts doc-codec does not yet parse; the grpprl cannot be walked past it without guessing");
|
|
47
|
+
return 1 + cb;
|
|
48
|
+
}
|
|
49
|
+
function readGrpprl(bytes) {
|
|
50
|
+
const prls = [];
|
|
51
|
+
let cursor = 0;
|
|
52
|
+
while (cursor < bytes.length) {
|
|
53
|
+
if (cursor + 2 > bytes.length) throw new require_errors.DocFormatError(`grpprl has ${bytes.length - cursor} trailing byte(s) at offset ${cursor}, too few for a sprm's own two`);
|
|
54
|
+
const sprm = decodeSprm(require_bytes.readUint16LE(bytes, cursor));
|
|
55
|
+
const size = operandSize(sprm, bytes, cursor + 2);
|
|
56
|
+
prls.push({
|
|
57
|
+
sprm,
|
|
58
|
+
operand: require_bytes.slice(bytes, cursor + 2, size, `operand of sprm 0x${sprm.value.toString(16)} at grpprl offset ${cursor}`)
|
|
59
|
+
});
|
|
60
|
+
cursor += 2 + size;
|
|
61
|
+
}
|
|
62
|
+
return prls;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
exports.SGC = SGC;
|
|
66
|
+
exports.decodeSprm = decodeSprm;
|
|
67
|
+
exports.operandSize = operandSize;
|
|
68
|
+
exports.readGrpprl = readGrpprl;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/prop/sprm.d.ts
|
|
2
|
+
interface Sprm {
|
|
3
|
+
/** The raw 16-bit opcode, which is what the property tables in [MS-DOC] 2.6.1-2.6.5 are keyed on. */
|
|
4
|
+
readonly value: number;
|
|
5
|
+
readonly ispmd: number;
|
|
6
|
+
readonly fSpec: 0 | 1;
|
|
7
|
+
readonly sgc: number;
|
|
8
|
+
readonly spra: number;
|
|
9
|
+
}
|
|
10
|
+
interface Prl {
|
|
11
|
+
readonly sprm: Sprm;
|
|
12
|
+
readonly operand: Uint8Array;
|
|
13
|
+
}
|
|
14
|
+
/** Sprm.sgc, [MS-DOC] 2.6.1 -- which property family the opcode belongs to. */
|
|
15
|
+
declare const SGC: {
|
|
16
|
+
readonly paragraph: 1;
|
|
17
|
+
readonly character: 2;
|
|
18
|
+
readonly picture: 3;
|
|
19
|
+
readonly section: 4;
|
|
20
|
+
readonly table: 5;
|
|
21
|
+
};
|
|
22
|
+
declare function decodeSprm(value: number): Sprm;
|
|
23
|
+
declare function operandSize(sprm: Sprm, bytes: Uint8Array, offset: number): number;
|
|
24
|
+
declare function readGrpprl(bytes: Uint8Array): Prl[];
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Prl, SGC, Sprm, decodeSprm, operandSize, readGrpprl };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/prop/sprm.d.ts
|
|
2
|
+
interface Sprm {
|
|
3
|
+
/** The raw 16-bit opcode, which is what the property tables in [MS-DOC] 2.6.1-2.6.5 are keyed on. */
|
|
4
|
+
readonly value: number;
|
|
5
|
+
readonly ispmd: number;
|
|
6
|
+
readonly fSpec: 0 | 1;
|
|
7
|
+
readonly sgc: number;
|
|
8
|
+
readonly spra: number;
|
|
9
|
+
}
|
|
10
|
+
interface Prl {
|
|
11
|
+
readonly sprm: Sprm;
|
|
12
|
+
readonly operand: Uint8Array;
|
|
13
|
+
}
|
|
14
|
+
/** Sprm.sgc, [MS-DOC] 2.6.1 -- which property family the opcode belongs to. */
|
|
15
|
+
declare const SGC: {
|
|
16
|
+
readonly paragraph: 1;
|
|
17
|
+
readonly character: 2;
|
|
18
|
+
readonly picture: 3;
|
|
19
|
+
readonly section: 4;
|
|
20
|
+
readonly table: 5;
|
|
21
|
+
};
|
|
22
|
+
declare function decodeSprm(value: number): Sprm;
|
|
23
|
+
declare function operandSize(sprm: Sprm, bytes: Uint8Array, offset: number): number;
|
|
24
|
+
declare function readGrpprl(bytes: Uint8Array): Prl[];
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Prl, SGC, Sprm, decodeSprm, operandSize, readGrpprl };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { DocFormatError, DocUnsupportedError } from "../errors.js";
|
|
2
|
+
import { readUint16LE, readUint8, slice } from "../bytes.js";
|
|
3
|
+
//#region src/prop/sprm.ts
|
|
4
|
+
/** Sprm.sgc, [MS-DOC] 2.6.1 -- which property family the opcode belongs to. */
|
|
5
|
+
const SGC = {
|
|
6
|
+
paragraph: 1,
|
|
7
|
+
character: 2,
|
|
8
|
+
picture: 3,
|
|
9
|
+
section: 4,
|
|
10
|
+
table: 5
|
|
11
|
+
};
|
|
12
|
+
const SPRM_T_DEF_TABLE = 54792;
|
|
13
|
+
const SPRM_P_CHG_TABS = 50709;
|
|
14
|
+
/** PChgTabsOperand.cb: "A value of 255 specifies that this instance of sprmPChgTabs MAY be ignored and that the size of the remainder of this operand ... is calculated by using the following formula". */
|
|
15
|
+
const P_CHG_TABS_COMPUTED_SIZE = 255;
|
|
16
|
+
function decodeSprm(value) {
|
|
17
|
+
return {
|
|
18
|
+
value,
|
|
19
|
+
ispmd: value & 511,
|
|
20
|
+
fSpec: (value >> 9 & 1) === 1 ? 1 : 0,
|
|
21
|
+
sgc: value >> 10 & 7,
|
|
22
|
+
spra: value >> 13 & 7
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function operandSize(sprm, bytes, offset) {
|
|
26
|
+
switch (sprm.spra) {
|
|
27
|
+
case 0:
|
|
28
|
+
case 1: return 1;
|
|
29
|
+
case 2:
|
|
30
|
+
case 4:
|
|
31
|
+
case 5: return 2;
|
|
32
|
+
case 3: return 4;
|
|
33
|
+
case 7: return 3;
|
|
34
|
+
case 6: return variableOperandSize(sprm, bytes, offset);
|
|
35
|
+
default: throw new DocFormatError(`sprm 0x${sprm.value.toString(16)} has spra ${sprm.spra}, which is outside the 0..7 range a 3-bit field can hold`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function variableOperandSize(sprm, bytes, offset) {
|
|
39
|
+
if (sprm.value === SPRM_T_DEF_TABLE) {
|
|
40
|
+
const cb = readUint16LE(bytes, offset);
|
|
41
|
+
if (cb < 1) throw new DocFormatError(`sprmTDefTable declares cb ${cb}, which cannot be a remainder length incremented by 1`);
|
|
42
|
+
return cb + 1;
|
|
43
|
+
}
|
|
44
|
+
const cb = readUint8(bytes, offset);
|
|
45
|
+
if (sprm.value === SPRM_P_CHG_TABS && cb === P_CHG_TABS_COMPUTED_SIZE) throw new DocUnsupportedError("this paragraph carries a sprmPChgTabs whose cb is the 255 sentinel, so its operand length is computed from tab-stop counts doc-codec does not yet parse; the grpprl cannot be walked past it without guessing");
|
|
46
|
+
return 1 + cb;
|
|
47
|
+
}
|
|
48
|
+
function readGrpprl(bytes) {
|
|
49
|
+
const prls = [];
|
|
50
|
+
let cursor = 0;
|
|
51
|
+
while (cursor < bytes.length) {
|
|
52
|
+
if (cursor + 2 > bytes.length) throw new DocFormatError(`grpprl has ${bytes.length - cursor} trailing byte(s) at offset ${cursor}, too few for a sprm's own two`);
|
|
53
|
+
const sprm = decodeSprm(readUint16LE(bytes, cursor));
|
|
54
|
+
const size = operandSize(sprm, bytes, cursor + 2);
|
|
55
|
+
prls.push({
|
|
56
|
+
sprm,
|
|
57
|
+
operand: slice(bytes, cursor + 2, size, `operand of sprm 0x${sprm.value.toString(16)} at grpprl offset ${cursor}`)
|
|
58
|
+
});
|
|
59
|
+
cursor += 2 + size;
|
|
60
|
+
}
|
|
61
|
+
return prls;
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { SGC, decodeSprm, operandSize, readGrpprl };
|
package/dist/read.cjs
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
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_detect = require("./detect.cjs");
|
|
5
|
+
const require_fib_fib = require("./fib/fib.cjs");
|
|
6
|
+
const require_text_piece_table = require("./text/piece-table.cjs");
|
|
7
|
+
const require_text_characters = require("./text/characters.cjs");
|
|
8
|
+
const require_text_special = require("./text/special.cjs");
|
|
9
|
+
const require_prop_sprm = require("./prop/sprm.cjs");
|
|
10
|
+
const require_prop_fkp = require("./prop/fkp.cjs");
|
|
11
|
+
const require_prop_chp = require("./prop/chp.cjs");
|
|
12
|
+
const require_prop_pap = require("./prop/pap.cjs");
|
|
13
|
+
const require_style_stsh = require("./style/stsh.cjs");
|
|
14
|
+
let archive_codec = require("archive-codec");
|
|
15
|
+
//#region src/read.ts
|
|
16
|
+
/** The page geometry every section is given, because this reader does not yet read a document's own. US Letter with one-inch margins is Word's own default for a new document; a document that states otherwise is not yet consulted, so this is a placeholder the schema requires rather than a fact read from the file. */
|
|
17
|
+
const DEFAULT_PAGE_SIZE = {
|
|
18
|
+
widthPt: 612,
|
|
19
|
+
heightPt: 792
|
|
20
|
+
};
|
|
21
|
+
const DEFAULT_MARGINS = {
|
|
22
|
+
topPt: 72,
|
|
23
|
+
rightPt: 72,
|
|
24
|
+
bottomPt: 72,
|
|
25
|
+
leftPt: 72
|
|
26
|
+
};
|
|
27
|
+
function readDocStreams(bytes) {
|
|
28
|
+
const streams = (0, archive_codec.readCompoundFile)(bytes);
|
|
29
|
+
const wordDocument = streams.find((stream) => stream.path === require_detect.WORD_DOCUMENT_STREAM);
|
|
30
|
+
if (wordDocument === void 0) throw new require_errors.DocFormatError(`this compound file has no "${require_detect.WORD_DOCUMENT_STREAM}" stream, so it is not a Word Binary File (it holds: ${streams.map((stream) => stream.path).join(", ")})`);
|
|
31
|
+
const fib = require_fib_fib.parseFib(wordDocument.bytes);
|
|
32
|
+
const wanted = require_fib_fib.tableStreamName(fib);
|
|
33
|
+
const table = streams.find((stream) => stream.path === wanted);
|
|
34
|
+
if (table === void 0) throw new require_errors.DocFormatError(`FibBase.fWhichTblStm selects the "${wanted}" stream, which this compound file does not contain`);
|
|
35
|
+
return {
|
|
36
|
+
wordDocument: wordDocument.bytes,
|
|
37
|
+
table: table.bytes,
|
|
38
|
+
fib
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function readDocContent(bytes) {
|
|
42
|
+
const { wordDocument, table, fib } = readDocStreams(bytes);
|
|
43
|
+
const pieceTable = require_text_piece_table.parseClx(require_bytes.slice(table, fib.fcClx, fib.lcbClx, "Clx in the Table stream"));
|
|
44
|
+
const styles = fib.lcbStshf > 0 ? require_style_stsh.parseStsh(require_bytes.slice(table, fib.fcStshf, fib.lcbStshf, "STSH in the Table stream")) : void 0;
|
|
45
|
+
const chpxTable = new require_prop_fkp.PropertyBinTable(wordDocument, require_bytes.slice(table, fib.fcPlcfBteChpx, fib.lcbPlcfBteChpx, "PlcBteChpx in the Table stream"), "PlcBteChpx");
|
|
46
|
+
const papxTable = new require_prop_fkp.PropertyBinTable(wordDocument, require_bytes.slice(table, fib.fcPlcfBtePapx, fib.lcbPlcfBtePapx, "PlcBtePapx in the Table stream"), "PlcBtePapx");
|
|
47
|
+
const range = require_text_characters.readTextRange(wordDocument, pieceTable, 0, fib.ccpText);
|
|
48
|
+
const blocks = readParagraphs(range.text, range.fcs, {
|
|
49
|
+
chpxTable,
|
|
50
|
+
papxTable,
|
|
51
|
+
styles,
|
|
52
|
+
characterProperties: /* @__PURE__ */ new Map()
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
kind: "wordprocessing",
|
|
56
|
+
metadata: {},
|
|
57
|
+
sections: [{
|
|
58
|
+
pageSize: DEFAULT_PAGE_SIZE,
|
|
59
|
+
margins: DEFAULT_MARGINS,
|
|
60
|
+
blocks
|
|
61
|
+
}]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function readParagraphs(text, fcs, context) {
|
|
65
|
+
const blocks = [];
|
|
66
|
+
let start = 0;
|
|
67
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
68
|
+
const code = text.charCodeAt(index);
|
|
69
|
+
if (!require_text_special.endsParagraph(code)) continue;
|
|
70
|
+
const markFc = fcs[index];
|
|
71
|
+
if (markFc === void 0) throw new require_errors.DocFormatError(`character ${index} has no byte offset, so its paragraph's properties cannot be located`);
|
|
72
|
+
blocks.push(buildParagraph(text.slice(start, index), fcs.slice(start, index), markFc, context));
|
|
73
|
+
start = index + 1;
|
|
74
|
+
}
|
|
75
|
+
if (start < text.length) {
|
|
76
|
+
const firstFc = fcs[start];
|
|
77
|
+
if (firstFc === void 0) throw new require_errors.DocFormatError(`character ${start} has no byte offset, so the trailing paragraph's properties cannot be located`);
|
|
78
|
+
blocks.push(buildParagraph(text.slice(start), fcs.slice(start), firstFc, context));
|
|
79
|
+
}
|
|
80
|
+
return blocks;
|
|
81
|
+
}
|
|
82
|
+
function buildParagraph(text, fcs, propertyFc, context) {
|
|
83
|
+
const papx = context.papxTable.papx(propertyFc);
|
|
84
|
+
const properties = {};
|
|
85
|
+
if (papx !== void 0) {
|
|
86
|
+
properties.istd = papx.istd;
|
|
87
|
+
require_prop_pap.applyParagraphSprms(require_prop_sprm.readGrpprl(papx.grpprl), properties);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
kind: "paragraph",
|
|
91
|
+
runs: buildRuns(text, fcs, context),
|
|
92
|
+
...paragraphAttributes(properties, context)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function paragraphAttributes(properties, context) {
|
|
96
|
+
const attributes = {};
|
|
97
|
+
const istd = properties.istd;
|
|
98
|
+
if (istd !== void 0) {
|
|
99
|
+
const style = context.styles?.styles[istd];
|
|
100
|
+
if (style !== void 0 && style.name !== "") attributes.styleId = style.name;
|
|
101
|
+
const headingLevel = require_style_stsh.headingLevelFromIstd(istd);
|
|
102
|
+
if (headingLevel !== void 0) attributes.headingLevel = headingLevel;
|
|
103
|
+
}
|
|
104
|
+
if (attributes.headingLevel === void 0 && properties.outlineLevel !== void 0) attributes.headingLevel = properties.outlineLevel + 1;
|
|
105
|
+
if (properties.alignment !== void 0) attributes.alignment = properties.alignment;
|
|
106
|
+
if (properties.spacingBeforePt !== void 0) attributes.spacingBeforePt = properties.spacingBeforePt;
|
|
107
|
+
if (properties.spacingAfterPt !== void 0) attributes.spacingAfterPt = properties.spacingAfterPt;
|
|
108
|
+
if (properties.lineSpacing !== void 0) attributes.lineSpacing = properties.lineSpacing;
|
|
109
|
+
if (properties.indentLeftPt !== void 0) attributes.indentLeftPt = properties.indentLeftPt;
|
|
110
|
+
if (properties.indentFirstLinePt !== void 0) attributes.indentFirstLinePt = properties.indentFirstLinePt;
|
|
111
|
+
if (properties.pageBreakBefore === true) attributes.pageBreakBefore = true;
|
|
112
|
+
if (properties.listId !== void 0) attributes.list = {
|
|
113
|
+
numId: String(properties.listId),
|
|
114
|
+
level: properties.listLevel ?? 0
|
|
115
|
+
};
|
|
116
|
+
return attributes;
|
|
117
|
+
}
|
|
118
|
+
function buildRuns(text, fcs, context) {
|
|
119
|
+
const runs = [];
|
|
120
|
+
let currentKey;
|
|
121
|
+
let currentText = "";
|
|
122
|
+
let currentProperties = {};
|
|
123
|
+
const enclosingInstruction = [];
|
|
124
|
+
let inInstruction = false;
|
|
125
|
+
const flush = () => {
|
|
126
|
+
if (currentText !== "") runs.push({
|
|
127
|
+
text: currentText,
|
|
128
|
+
...currentProperties
|
|
129
|
+
});
|
|
130
|
+
currentText = "";
|
|
131
|
+
};
|
|
132
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
133
|
+
const code = text.charCodeAt(index);
|
|
134
|
+
if (code === 19) {
|
|
135
|
+
flush();
|
|
136
|
+
enclosingInstruction.push(inInstruction);
|
|
137
|
+
inInstruction = true;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (code === 20) {
|
|
141
|
+
inInstruction = false;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (code === 21) {
|
|
145
|
+
inInstruction = enclosingInstruction.pop() ?? inInstruction;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (inInstruction || require_text_special.isAnchorOnly(code)) continue;
|
|
149
|
+
const fc = fcs[index];
|
|
150
|
+
if (fc === void 0) throw new require_errors.DocFormatError(`character ${index} of a paragraph has no byte offset, so its formatting cannot be located`);
|
|
151
|
+
const grpprl = context.chpxTable.chpxGrpprl(fc);
|
|
152
|
+
const key = grpprl === void 0 ? "none" : `${grpprl.byteOffset}:${grpprl.byteLength}`;
|
|
153
|
+
if (key !== currentKey) {
|
|
154
|
+
flush();
|
|
155
|
+
currentKey = key;
|
|
156
|
+
let properties = context.characterProperties.get(key);
|
|
157
|
+
if (properties === void 0) {
|
|
158
|
+
properties = {};
|
|
159
|
+
if (grpprl !== void 0) require_prop_chp.applyCharacterSprms(require_prop_sprm.readGrpprl(grpprl), properties);
|
|
160
|
+
context.characterProperties.set(key, properties);
|
|
161
|
+
}
|
|
162
|
+
currentProperties = properties;
|
|
163
|
+
}
|
|
164
|
+
currentText += String.fromCharCode(code === 11 ? 10 : code);
|
|
165
|
+
}
|
|
166
|
+
flush();
|
|
167
|
+
return runs;
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
exports.readDocContent = readDocContent;
|
|
171
|
+
exports.readDocStreams = readDocStreams;
|
package/dist/read.d.cts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Fib } from "./fib/fib.cjs";
|
|
2
|
+
import { ContentDocument } from "document-schema.js";
|
|
3
|
+
//#region src/read.d.ts
|
|
4
|
+
interface DocStreams {
|
|
5
|
+
readonly wordDocument: Uint8Array;
|
|
6
|
+
readonly table: Uint8Array;
|
|
7
|
+
readonly fib: Fib;
|
|
8
|
+
}
|
|
9
|
+
declare function readDocStreams(bytes: Uint8Array<ArrayBuffer>): DocStreams;
|
|
10
|
+
declare function readDocContent(bytes: Uint8Array<ArrayBuffer>): ContentDocument;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { DocStreams, readDocContent, readDocStreams };
|
package/dist/read.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Fib } from "./fib/fib.js";
|
|
2
|
+
import { ContentDocument } from "document-schema.js";
|
|
3
|
+
//#region src/read.d.ts
|
|
4
|
+
interface DocStreams {
|
|
5
|
+
readonly wordDocument: Uint8Array;
|
|
6
|
+
readonly table: Uint8Array;
|
|
7
|
+
readonly fib: Fib;
|
|
8
|
+
}
|
|
9
|
+
declare function readDocStreams(bytes: Uint8Array<ArrayBuffer>): DocStreams;
|
|
10
|
+
declare function readDocContent(bytes: Uint8Array<ArrayBuffer>): ContentDocument;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { DocStreams, readDocContent, readDocStreams };
|