doc-codec 2.4.3 → 2.6.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/README.md +53 -20
- package/dist/chp-B6AOh85P.d.ts +18 -0
- package/dist/chp-BpCqpb7V.d.cts +18 -0
- package/dist/detect.cjs +3 -0
- package/dist/detect.d.cts +3 -1
- package/dist/detect.d.ts +3 -1
- package/dist/detect.js +3 -1
- package/dist/encryption.cjs +44 -10
- package/dist/encryption.d.cts +2 -2
- package/dist/encryption.d.ts +2 -2
- package/dist/encryption.js +45 -11
- package/dist/fib/fib.cjs +14 -0
- package/dist/fib/fib.d.cts +1 -1
- package/dist/fib/fib.d.ts +1 -1
- package/dist/fib/fib.js +14 -0
- package/dist/fib/offsets.cjs +14 -0
- package/dist/fib/offsets.d.cts +14 -0
- package/dist/fib/offsets.d.ts +14 -0
- package/dist/fib/offsets.js +14 -0
- package/dist/{fib-DWFta19r.d.cts → fib-6BFIx153.d.cts} +14 -0
- package/dist/{fib-DWFta19r.d.ts → fib-6BFIx153.d.ts} +14 -0
- package/dist/fkp-I-laf-8y.d.cts +33 -0
- package/dist/fkp-I-laf-8y.d.ts +33 -0
- package/dist/headers-footers.cjs +34 -0
- package/dist/headers-footers.d.cts +16 -0
- package/dist/headers-footers.d.ts +16 -0
- package/dist/headers-footers.js +33 -0
- package/dist/index.cjs +20 -9
- package/dist/index.d.cts +16 -12
- package/dist/index.d.ts +16 -12
- package/dist/index.js +15 -11
- package/dist/list/numbering-write.d.cts +1 -1
- package/dist/list/numbering-write.d.ts +1 -1
- package/dist/list/numbering.d.cts +23 -1
- package/dist/list/numbering.d.ts +23 -1
- package/dist/notes.cjs +26 -0
- package/dist/notes.d.cts +20 -0
- package/dist/notes.d.ts +20 -0
- package/dist/notes.js +25 -0
- package/dist/{pap-BT1p0uzu.d.cts → pap-BQ2E17K9.d.cts} +5 -3
- package/dist/{pap-DOFLMWcG.d.ts → pap-iWFIpCdo.d.ts} +5 -3
- package/dist/paragraphs-DQUp8_HB.d.cts +31 -0
- package/dist/paragraphs-s2lJ6w2I.d.ts +31 -0
- package/dist/pictures.cjs +93 -0
- package/dist/pictures.d.cts +6 -0
- package/dist/pictures.d.ts +6 -0
- package/dist/pictures.js +92 -0
- package/dist/piece-table-CA18mhjv.d.cts +28 -0
- package/dist/piece-table-CA18mhjv.d.ts +28 -0
- package/dist/prop/chp.d.cts +1 -17
- package/dist/prop/chp.d.ts +1 -17
- package/dist/prop/fkp.d.cts +1 -32
- package/dist/prop/fkp.d.ts +1 -32
- package/dist/prop/pap.cjs +4 -2
- package/dist/prop/pap.d.cts +1 -1
- package/dist/prop/pap.d.ts +1 -1
- package/dist/prop/pap.js +4 -2
- package/dist/prop/sep.cjs +20 -10
- package/dist/prop/sep.d.cts +8 -4
- package/dist/prop/sep.d.ts +8 -4
- package/dist/prop/sep.js +20 -10
- package/dist/read.cjs +48 -153
- package/dist/read.d.cts +13 -15
- package/dist/read.d.ts +13 -15
- package/dist/read.js +51 -156
- package/dist/stsh-Bj0YSEOp.d.ts +40 -0
- package/dist/stsh-CK59x6XG.d.cts +40 -0
- package/dist/style/stsh.d.cts +1 -39
- package/dist/style/stsh.d.ts +1 -39
- package/dist/subdocument.cjs +20 -0
- package/dist/subdocument.d.cts +8 -0
- package/dist/subdocument.d.ts +8 -0
- package/dist/subdocument.js +18 -0
- package/dist/table/read.cjs +31 -17
- package/dist/table/read.d.cts +1 -1
- package/dist/table/read.d.ts +1 -1
- package/dist/table/read.js +32 -18
- package/dist/text/characters.d.cts +1 -1
- package/dist/text/characters.d.ts +1 -1
- package/dist/text/paragraphs.cjs +189 -0
- package/dist/text/paragraphs.d.cts +2 -0
- package/dist/text/paragraphs.d.ts +2 -0
- package/dist/text/paragraphs.js +187 -0
- package/dist/text/piece-table.d.cts +1 -27
- package/dist/text/piece-table.d.ts +1 -27
- package/dist/write.cjs +3 -3
- package/dist/write.js +3 -3
- package/package.json +2 -2
- package/dist/numbering-DSpm9w6y.d.cts +0 -24
- package/dist/numbering-DayjnHXY.d.ts +0 -24
package/dist/table/read.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DocFormatError
|
|
1
|
+
import { DocFormatError } from "../errors.js";
|
|
2
2
|
import "../text/special.js";
|
|
3
3
|
import { cellBordersFrom } from "./decoration.js";
|
|
4
4
|
import { applyTableSprms } from "./tap.js";
|
|
@@ -22,30 +22,44 @@ function effectiveColumnBoundaryTolerance(definitions) {
|
|
|
22
22
|
return narrowestRealGapTwips === void 0 ? TWIPS_PER_POINT : Math.min(TWIPS_PER_POINT, narrowestRealGapTwips - 1);
|
|
23
23
|
}
|
|
24
24
|
function assembleBlocks(entries) {
|
|
25
|
+
return walkBlocksAtDepth(entries, 0);
|
|
26
|
+
}
|
|
27
|
+
function effectiveTableDepth(properties) {
|
|
28
|
+
if (properties.tableDepth !== void 0) return properties.tableDepth;
|
|
29
|
+
return properties.inTable === true ? 1 : 0;
|
|
30
|
+
}
|
|
31
|
+
function isCellBoundary(entry, tableDepth) {
|
|
32
|
+
if (tableDepth === 1) return entry.terminator === 7 && entry.properties.tableRowEnd !== true;
|
|
33
|
+
return entry.terminator === 13 && entry.properties.innerTableCellMark === true;
|
|
34
|
+
}
|
|
35
|
+
function isRowBoundary(entry, tableDepth) {
|
|
36
|
+
return tableDepth === 1 ? entry.properties.tableRowEnd === true : entry.properties.innerTtpMark === true;
|
|
37
|
+
}
|
|
38
|
+
function walkBlocksAtDepth(entries, depth) {
|
|
25
39
|
const blocks = [];
|
|
26
40
|
let index = 0;
|
|
27
41
|
while (index < entries.length) {
|
|
28
42
|
const entry = entries[index];
|
|
29
43
|
if (entry === void 0) break;
|
|
30
|
-
if (entry.properties
|
|
31
|
-
blocks.push(entry.
|
|
44
|
+
if (effectiveTableDepth(entry.properties) <= depth) {
|
|
45
|
+
blocks.push(...entry.blocks);
|
|
32
46
|
index += 1;
|
|
33
47
|
continue;
|
|
34
48
|
}
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
49
|
+
const tableDepth = depth + 1;
|
|
50
|
+
const { runEntries, nextIndex } = collectTableRun(entries, index, tableDepth);
|
|
51
|
+
const table = tryAssembleTable(runEntries, tableDepth);
|
|
52
|
+
blocks.push(...table !== void 0 ? [table] : runEntries.flatMap((run) => run.blocks));
|
|
38
53
|
index = nextIndex;
|
|
39
54
|
}
|
|
40
55
|
return blocks;
|
|
41
56
|
}
|
|
42
|
-
function collectTableRun(entries, start) {
|
|
57
|
+
function collectTableRun(entries, start, tableDepth) {
|
|
43
58
|
const runEntries = [];
|
|
44
59
|
let index = start;
|
|
45
60
|
while (index < entries.length) {
|
|
46
61
|
const entry = entries[index];
|
|
47
|
-
if (entry
|
|
48
|
-
if (entry.properties.tableDepth !== void 0 && entry.properties.tableDepth > 1 || entry.properties.nestedTableMark === true) throw new DocUnsupportedError("doc-codec does not support a table nested inside a table cell (table depth greater than 1)");
|
|
62
|
+
if (entry === void 0 || effectiveTableDepth(entry.properties) < tableDepth) break;
|
|
49
63
|
runEntries.push(entry);
|
|
50
64
|
index += 1;
|
|
51
65
|
}
|
|
@@ -54,15 +68,15 @@ function collectTableRun(entries, start) {
|
|
|
54
68
|
nextIndex: index
|
|
55
69
|
};
|
|
56
70
|
}
|
|
57
|
-
function tryAssembleTable(runEntries) {
|
|
71
|
+
function tryAssembleTable(runEntries, tableDepth) {
|
|
58
72
|
const rawRows = [];
|
|
59
73
|
const rowDefinitions = [];
|
|
60
74
|
const rowHeights = [];
|
|
61
|
-
let
|
|
75
|
+
let cellEntries = [];
|
|
62
76
|
let rowCells = [];
|
|
63
77
|
for (const entry of runEntries) {
|
|
64
|
-
|
|
65
|
-
if (entry
|
|
78
|
+
cellEntries.push(entry);
|
|
79
|
+
if (isRowBoundary(entry, tableDepth)) {
|
|
66
80
|
const rowProperties = applyTableSprms(entry.grpprl, {});
|
|
67
81
|
const definition = rowProperties.definition;
|
|
68
82
|
if (definition === void 0) return void 0;
|
|
@@ -82,15 +96,15 @@ function tryAssembleTable(runEntries) {
|
|
|
82
96
|
rowDefinitions.push(definition);
|
|
83
97
|
rowHeights.push(rowProperties.heightPt);
|
|
84
98
|
rowCells = [];
|
|
85
|
-
|
|
99
|
+
cellEntries = [];
|
|
86
100
|
continue;
|
|
87
101
|
}
|
|
88
|
-
if (entry
|
|
89
|
-
rowCells.push({ blocks:
|
|
90
|
-
|
|
102
|
+
if (isCellBoundary(entry, tableDepth)) {
|
|
103
|
+
rowCells.push({ blocks: walkBlocksAtDepth(cellEntries, tableDepth) });
|
|
104
|
+
cellEntries = [];
|
|
91
105
|
}
|
|
92
106
|
}
|
|
93
|
-
if (
|
|
107
|
+
if (cellEntries.length > 0 || rowCells.length > 0) throw new DocFormatError("a table's paragraphs end without a row-ending mark to close the row's last cell");
|
|
94
108
|
const toleranceTwips = effectiveColumnBoundaryTolerance(rowDefinitions);
|
|
95
109
|
const columnBoundariesTwips = canonicalColumnBoundariesTwips(rowDefinitions, toleranceTwips);
|
|
96
110
|
return {
|
|
@@ -0,0 +1,189 @@
|
|
|
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_prop_sprm = require("../prop/sprm.cjs");
|
|
5
|
+
const require_prop_chp = require("../prop/chp.cjs");
|
|
6
|
+
const require_prop_pap = require("../prop/pap.cjs");
|
|
7
|
+
const require_pictures = require("../pictures.cjs");
|
|
8
|
+
const require_style_stsh = require("../style/stsh.cjs");
|
|
9
|
+
const require_text_special = require("./special.cjs");
|
|
10
|
+
//#region src/text/paragraphs.ts
|
|
11
|
+
function readParagraphs(text, fcs, context) {
|
|
12
|
+
const entries = [];
|
|
13
|
+
let start = 0;
|
|
14
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
15
|
+
const code = text.charCodeAt(index);
|
|
16
|
+
if (!require_text_special.endsParagraph(code)) continue;
|
|
17
|
+
const markFc = fcs[index];
|
|
18
|
+
if (markFc === void 0) throw new require_errors.DocFormatError(`character ${index} has no byte offset, so its paragraph's properties cannot be located`);
|
|
19
|
+
entries.push(buildParagraph(text.slice(start, index), fcs.slice(start, index), markFc, code, index + 1, context));
|
|
20
|
+
start = index + 1;
|
|
21
|
+
}
|
|
22
|
+
if (start < text.length) {
|
|
23
|
+
const firstFc = fcs[start];
|
|
24
|
+
if (firstFc === void 0) throw new require_errors.DocFormatError(`character ${start} has no byte offset, so the trailing paragraph's properties cannot be located`);
|
|
25
|
+
entries.push(buildParagraph(text.slice(start), fcs.slice(start), firstFc, 13, text.length, context));
|
|
26
|
+
}
|
|
27
|
+
return entries;
|
|
28
|
+
}
|
|
29
|
+
function buildParagraph(text, fcs, propertyFc, terminator, endCp, context) {
|
|
30
|
+
const papx = context.papxTable.papx(propertyFc);
|
|
31
|
+
const properties = {};
|
|
32
|
+
const grpprl = papx !== void 0 ? require_prop_sprm.readGrpprl(papx.grpprl) : [];
|
|
33
|
+
if (papx !== void 0) {
|
|
34
|
+
properties.istd = papx.istd;
|
|
35
|
+
if (context.styles !== void 0) require_prop_pap.applyParagraphSprms(require_style_stsh.resolveStyleFormatting(context.styles, papx.istd).paragraphPrls, properties);
|
|
36
|
+
require_prop_pap.applyParagraphSprms(grpprl, properties);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
blocks: buildParagraphBlocks(text, fcs, context, papx?.istd, paragraphAttributes(properties, context)),
|
|
40
|
+
properties,
|
|
41
|
+
grpprl,
|
|
42
|
+
terminator,
|
|
43
|
+
endCp
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function buildParagraphBlocks(text, fcs, context, paragraphIstd, attributes) {
|
|
47
|
+
const blocks = [];
|
|
48
|
+
let segmentStart = 0;
|
|
49
|
+
const flushSegment = (end) => {
|
|
50
|
+
if (end <= segmentStart) return;
|
|
51
|
+
const runs = buildRuns(text.slice(segmentStart, end), fcs.slice(segmentStart, end), context, paragraphIstd);
|
|
52
|
+
blocks.push({
|
|
53
|
+
kind: "paragraph",
|
|
54
|
+
runs,
|
|
55
|
+
...attributes
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
59
|
+
if (text.charCodeAt(index) !== 1) continue;
|
|
60
|
+
flushSegment(index);
|
|
61
|
+
const fc = fcs[index];
|
|
62
|
+
const image = fc === void 0 ? void 0 : resolveInlinePicture(context, fc);
|
|
63
|
+
if (image !== void 0) blocks.push(image);
|
|
64
|
+
segmentStart = index + 1;
|
|
65
|
+
}
|
|
66
|
+
flushSegment(text.length);
|
|
67
|
+
if (blocks.length === 0) blocks.push({
|
|
68
|
+
kind: "paragraph",
|
|
69
|
+
runs: [],
|
|
70
|
+
...attributes
|
|
71
|
+
});
|
|
72
|
+
return blocks;
|
|
73
|
+
}
|
|
74
|
+
const SPRM_C_PIC_LOCATION = 27139;
|
|
75
|
+
function resolveInlinePicture(context, fc) {
|
|
76
|
+
if (context.dataStream === void 0) return void 0;
|
|
77
|
+
const grpprl = context.chpxTable.chpxGrpprl(fc);
|
|
78
|
+
if (grpprl === void 0) return void 0;
|
|
79
|
+
for (const prl of require_prop_sprm.readGrpprl(grpprl)) {
|
|
80
|
+
if (prl.sprm.value !== SPRM_C_PIC_LOCATION) continue;
|
|
81
|
+
const picLocation = require_bytes.readInt32LE(prl.operand, 0);
|
|
82
|
+
return require_pictures.readInlinePicture(context.dataStream, picLocation);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function paragraphAttributes(properties, context) {
|
|
86
|
+
const attributes = {};
|
|
87
|
+
const istd = properties.istd;
|
|
88
|
+
if (istd !== void 0) {
|
|
89
|
+
const style = context.styles?.styles[istd];
|
|
90
|
+
if (style !== void 0 && style.name !== "") attributes.styleId = style.name;
|
|
91
|
+
const headingLevel = require_style_stsh.headingLevelFromIstd(istd);
|
|
92
|
+
if (headingLevel !== void 0) attributes.headingLevel = headingLevel;
|
|
93
|
+
}
|
|
94
|
+
if (attributes.headingLevel === void 0 && properties.outlineLevel !== void 0) attributes.headingLevel = properties.outlineLevel + 1;
|
|
95
|
+
if (properties.alignment !== void 0) attributes.alignment = properties.alignment;
|
|
96
|
+
if (properties.spacingBeforePt !== void 0) attributes.spacingBeforePt = properties.spacingBeforePt;
|
|
97
|
+
if (properties.spacingAfterPt !== void 0) attributes.spacingAfterPt = properties.spacingAfterPt;
|
|
98
|
+
if (properties.lineSpacing !== void 0) attributes.lineSpacing = properties.lineSpacing;
|
|
99
|
+
if (properties.indentLeftPt !== void 0) attributes.indentLeftPt = properties.indentLeftPt;
|
|
100
|
+
if (properties.indentRightPt !== void 0) attributes.indentRightPt = properties.indentRightPt;
|
|
101
|
+
if (properties.indentFirstLinePt !== void 0) attributes.indentFirstLinePt = properties.indentFirstLinePt;
|
|
102
|
+
if (properties.pageBreakBefore === true) attributes.pageBreakBefore = true;
|
|
103
|
+
if (properties.listId !== void 0) attributes.list = {
|
|
104
|
+
numId: String(properties.listId),
|
|
105
|
+
level: properties.listLevel ?? 0
|
|
106
|
+
};
|
|
107
|
+
return attributes;
|
|
108
|
+
}
|
|
109
|
+
function buildRuns(text, fcs, context, paragraphIstd) {
|
|
110
|
+
const runs = [];
|
|
111
|
+
let currentKey;
|
|
112
|
+
let currentText = "";
|
|
113
|
+
let currentProperties = {};
|
|
114
|
+
const paragraphStyleCharacterPrls = context.styles !== void 0 && paragraphIstd !== void 0 ? require_style_stsh.resolveStyleFormatting(context.styles, paragraphIstd).characterPrls : [];
|
|
115
|
+
const enclosingInstruction = [];
|
|
116
|
+
let inInstruction = false;
|
|
117
|
+
const flush = () => {
|
|
118
|
+
if (currentText !== "") runs.push({
|
|
119
|
+
text: currentText,
|
|
120
|
+
...currentProperties
|
|
121
|
+
});
|
|
122
|
+
currentText = "";
|
|
123
|
+
};
|
|
124
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
125
|
+
const code = text.charCodeAt(index);
|
|
126
|
+
if (code === 19) {
|
|
127
|
+
flush();
|
|
128
|
+
enclosingInstruction.push(inInstruction);
|
|
129
|
+
inInstruction = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (code === 20) {
|
|
133
|
+
inInstruction = false;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (code === 21) {
|
|
137
|
+
inInstruction = enclosingInstruction.pop() ?? inInstruction;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (inInstruction || require_text_special.isAnchorOnly(code)) continue;
|
|
141
|
+
const fc = fcs[index];
|
|
142
|
+
if (fc === void 0) throw new require_errors.DocFormatError(`character ${index} of a paragraph has no byte offset, so its formatting cannot be located`);
|
|
143
|
+
const grpprl = context.chpxTable.chpxGrpprl(fc);
|
|
144
|
+
const chpxKey = grpprl === void 0 ? "none" : `${grpprl.byteOffset}:${grpprl.byteLength}`;
|
|
145
|
+
const key = `${paragraphIstd ?? "none"}:${chpxKey}`;
|
|
146
|
+
if (key !== currentKey) {
|
|
147
|
+
flush();
|
|
148
|
+
currentKey = key;
|
|
149
|
+
let properties = context.characterProperties.get(key);
|
|
150
|
+
if (properties === void 0) {
|
|
151
|
+
properties = {};
|
|
152
|
+
require_prop_chp.applyCharacterSprms(paragraphStyleCharacterPrls, properties, context.fonts);
|
|
153
|
+
if (grpprl !== void 0) {
|
|
154
|
+
const runPrls = require_prop_sprm.readGrpprl(grpprl);
|
|
155
|
+
const characterIstd = require_prop_chp.characterIstdFromGrpprl(runPrls);
|
|
156
|
+
if (characterIstd !== void 0 && context.styles !== void 0) require_prop_chp.applyCharacterSprms(require_style_stsh.resolveStyleFormatting(context.styles, characterIstd).characterPrls, properties, context.fonts);
|
|
157
|
+
require_prop_chp.applyCharacterSprms(runPrls, properties, context.fonts);
|
|
158
|
+
}
|
|
159
|
+
context.characterProperties.set(key, properties);
|
|
160
|
+
}
|
|
161
|
+
currentProperties = properties;
|
|
162
|
+
}
|
|
163
|
+
currentText += String.fromCharCode(code === 11 ? 10 : code);
|
|
164
|
+
}
|
|
165
|
+
flush();
|
|
166
|
+
return runs;
|
|
167
|
+
}
|
|
168
|
+
function splitEntriesByBoundaries(entries, boundaries) {
|
|
169
|
+
const groupCount = Math.max(boundaries.length - 1, 0);
|
|
170
|
+
const groups = Array.from({ length: groupCount }, () => []);
|
|
171
|
+
let index = 0;
|
|
172
|
+
const advancePastEmptyGroups = () => {
|
|
173
|
+
while (index < groupCount && boundaries[index] === boundaries[index + 1]) index += 1;
|
|
174
|
+
};
|
|
175
|
+
advancePastEmptyGroups();
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
const group = groups[index];
|
|
178
|
+
if (group === void 0) break;
|
|
179
|
+
group.push(entry);
|
|
180
|
+
if (entry.endCp === boundaries[index + 1]) {
|
|
181
|
+
index += 1;
|
|
182
|
+
advancePastEmptyGroups();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return groups;
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
exports.readParagraphs = readParagraphs;
|
|
189
|
+
exports.splitEntriesByBoundaries = splitEntriesByBoundaries;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { DocFormatError } from "../errors.js";
|
|
2
|
+
import { readInt32LE } from "../bytes.js";
|
|
3
|
+
import { readGrpprl } from "../prop/sprm.js";
|
|
4
|
+
import { applyCharacterSprms, characterIstdFromGrpprl } from "../prop/chp.js";
|
|
5
|
+
import { applyParagraphSprms } from "../prop/pap.js";
|
|
6
|
+
import { readInlinePicture } from "../pictures.js";
|
|
7
|
+
import { headingLevelFromIstd, resolveStyleFormatting } from "../style/stsh.js";
|
|
8
|
+
import { endsParagraph, isAnchorOnly } from "./special.js";
|
|
9
|
+
//#region src/text/paragraphs.ts
|
|
10
|
+
function readParagraphs(text, fcs, context) {
|
|
11
|
+
const entries = [];
|
|
12
|
+
let start = 0;
|
|
13
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
14
|
+
const code = text.charCodeAt(index);
|
|
15
|
+
if (!endsParagraph(code)) continue;
|
|
16
|
+
const markFc = fcs[index];
|
|
17
|
+
if (markFc === void 0) throw new DocFormatError(`character ${index} has no byte offset, so its paragraph's properties cannot be located`);
|
|
18
|
+
entries.push(buildParagraph(text.slice(start, index), fcs.slice(start, index), markFc, code, index + 1, context));
|
|
19
|
+
start = index + 1;
|
|
20
|
+
}
|
|
21
|
+
if (start < text.length) {
|
|
22
|
+
const firstFc = fcs[start];
|
|
23
|
+
if (firstFc === void 0) throw new DocFormatError(`character ${start} has no byte offset, so the trailing paragraph's properties cannot be located`);
|
|
24
|
+
entries.push(buildParagraph(text.slice(start), fcs.slice(start), firstFc, 13, text.length, context));
|
|
25
|
+
}
|
|
26
|
+
return entries;
|
|
27
|
+
}
|
|
28
|
+
function buildParagraph(text, fcs, propertyFc, terminator, endCp, context) {
|
|
29
|
+
const papx = context.papxTable.papx(propertyFc);
|
|
30
|
+
const properties = {};
|
|
31
|
+
const grpprl = papx !== void 0 ? readGrpprl(papx.grpprl) : [];
|
|
32
|
+
if (papx !== void 0) {
|
|
33
|
+
properties.istd = papx.istd;
|
|
34
|
+
if (context.styles !== void 0) applyParagraphSprms(resolveStyleFormatting(context.styles, papx.istd).paragraphPrls, properties);
|
|
35
|
+
applyParagraphSprms(grpprl, properties);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
blocks: buildParagraphBlocks(text, fcs, context, papx?.istd, paragraphAttributes(properties, context)),
|
|
39
|
+
properties,
|
|
40
|
+
grpprl,
|
|
41
|
+
terminator,
|
|
42
|
+
endCp
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function buildParagraphBlocks(text, fcs, context, paragraphIstd, attributes) {
|
|
46
|
+
const blocks = [];
|
|
47
|
+
let segmentStart = 0;
|
|
48
|
+
const flushSegment = (end) => {
|
|
49
|
+
if (end <= segmentStart) return;
|
|
50
|
+
const runs = buildRuns(text.slice(segmentStart, end), fcs.slice(segmentStart, end), context, paragraphIstd);
|
|
51
|
+
blocks.push({
|
|
52
|
+
kind: "paragraph",
|
|
53
|
+
runs,
|
|
54
|
+
...attributes
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
58
|
+
if (text.charCodeAt(index) !== 1) continue;
|
|
59
|
+
flushSegment(index);
|
|
60
|
+
const fc = fcs[index];
|
|
61
|
+
const image = fc === void 0 ? void 0 : resolveInlinePicture(context, fc);
|
|
62
|
+
if (image !== void 0) blocks.push(image);
|
|
63
|
+
segmentStart = index + 1;
|
|
64
|
+
}
|
|
65
|
+
flushSegment(text.length);
|
|
66
|
+
if (blocks.length === 0) blocks.push({
|
|
67
|
+
kind: "paragraph",
|
|
68
|
+
runs: [],
|
|
69
|
+
...attributes
|
|
70
|
+
});
|
|
71
|
+
return blocks;
|
|
72
|
+
}
|
|
73
|
+
const SPRM_C_PIC_LOCATION = 27139;
|
|
74
|
+
function resolveInlinePicture(context, fc) {
|
|
75
|
+
if (context.dataStream === void 0) return void 0;
|
|
76
|
+
const grpprl = context.chpxTable.chpxGrpprl(fc);
|
|
77
|
+
if (grpprl === void 0) return void 0;
|
|
78
|
+
for (const prl of readGrpprl(grpprl)) {
|
|
79
|
+
if (prl.sprm.value !== SPRM_C_PIC_LOCATION) continue;
|
|
80
|
+
const picLocation = readInt32LE(prl.operand, 0);
|
|
81
|
+
return readInlinePicture(context.dataStream, picLocation);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function paragraphAttributes(properties, context) {
|
|
85
|
+
const attributes = {};
|
|
86
|
+
const istd = properties.istd;
|
|
87
|
+
if (istd !== void 0) {
|
|
88
|
+
const style = context.styles?.styles[istd];
|
|
89
|
+
if (style !== void 0 && style.name !== "") attributes.styleId = style.name;
|
|
90
|
+
const headingLevel = headingLevelFromIstd(istd);
|
|
91
|
+
if (headingLevel !== void 0) attributes.headingLevel = headingLevel;
|
|
92
|
+
}
|
|
93
|
+
if (attributes.headingLevel === void 0 && properties.outlineLevel !== void 0) attributes.headingLevel = properties.outlineLevel + 1;
|
|
94
|
+
if (properties.alignment !== void 0) attributes.alignment = properties.alignment;
|
|
95
|
+
if (properties.spacingBeforePt !== void 0) attributes.spacingBeforePt = properties.spacingBeforePt;
|
|
96
|
+
if (properties.spacingAfterPt !== void 0) attributes.spacingAfterPt = properties.spacingAfterPt;
|
|
97
|
+
if (properties.lineSpacing !== void 0) attributes.lineSpacing = properties.lineSpacing;
|
|
98
|
+
if (properties.indentLeftPt !== void 0) attributes.indentLeftPt = properties.indentLeftPt;
|
|
99
|
+
if (properties.indentRightPt !== void 0) attributes.indentRightPt = properties.indentRightPt;
|
|
100
|
+
if (properties.indentFirstLinePt !== void 0) attributes.indentFirstLinePt = properties.indentFirstLinePt;
|
|
101
|
+
if (properties.pageBreakBefore === true) attributes.pageBreakBefore = true;
|
|
102
|
+
if (properties.listId !== void 0) attributes.list = {
|
|
103
|
+
numId: String(properties.listId),
|
|
104
|
+
level: properties.listLevel ?? 0
|
|
105
|
+
};
|
|
106
|
+
return attributes;
|
|
107
|
+
}
|
|
108
|
+
function buildRuns(text, fcs, context, paragraphIstd) {
|
|
109
|
+
const runs = [];
|
|
110
|
+
let currentKey;
|
|
111
|
+
let currentText = "";
|
|
112
|
+
let currentProperties = {};
|
|
113
|
+
const paragraphStyleCharacterPrls = context.styles !== void 0 && paragraphIstd !== void 0 ? resolveStyleFormatting(context.styles, paragraphIstd).characterPrls : [];
|
|
114
|
+
const enclosingInstruction = [];
|
|
115
|
+
let inInstruction = false;
|
|
116
|
+
const flush = () => {
|
|
117
|
+
if (currentText !== "") runs.push({
|
|
118
|
+
text: currentText,
|
|
119
|
+
...currentProperties
|
|
120
|
+
});
|
|
121
|
+
currentText = "";
|
|
122
|
+
};
|
|
123
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
124
|
+
const code = text.charCodeAt(index);
|
|
125
|
+
if (code === 19) {
|
|
126
|
+
flush();
|
|
127
|
+
enclosingInstruction.push(inInstruction);
|
|
128
|
+
inInstruction = true;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (code === 20) {
|
|
132
|
+
inInstruction = false;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (code === 21) {
|
|
136
|
+
inInstruction = enclosingInstruction.pop() ?? inInstruction;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (inInstruction || isAnchorOnly(code)) continue;
|
|
140
|
+
const fc = fcs[index];
|
|
141
|
+
if (fc === void 0) throw new DocFormatError(`character ${index} of a paragraph has no byte offset, so its formatting cannot be located`);
|
|
142
|
+
const grpprl = context.chpxTable.chpxGrpprl(fc);
|
|
143
|
+
const chpxKey = grpprl === void 0 ? "none" : `${grpprl.byteOffset}:${grpprl.byteLength}`;
|
|
144
|
+
const key = `${paragraphIstd ?? "none"}:${chpxKey}`;
|
|
145
|
+
if (key !== currentKey) {
|
|
146
|
+
flush();
|
|
147
|
+
currentKey = key;
|
|
148
|
+
let properties = context.characterProperties.get(key);
|
|
149
|
+
if (properties === void 0) {
|
|
150
|
+
properties = {};
|
|
151
|
+
applyCharacterSprms(paragraphStyleCharacterPrls, properties, context.fonts);
|
|
152
|
+
if (grpprl !== void 0) {
|
|
153
|
+
const runPrls = readGrpprl(grpprl);
|
|
154
|
+
const characterIstd = characterIstdFromGrpprl(runPrls);
|
|
155
|
+
if (characterIstd !== void 0 && context.styles !== void 0) applyCharacterSprms(resolveStyleFormatting(context.styles, characterIstd).characterPrls, properties, context.fonts);
|
|
156
|
+
applyCharacterSprms(runPrls, properties, context.fonts);
|
|
157
|
+
}
|
|
158
|
+
context.characterProperties.set(key, properties);
|
|
159
|
+
}
|
|
160
|
+
currentProperties = properties;
|
|
161
|
+
}
|
|
162
|
+
currentText += String.fromCharCode(code === 11 ? 10 : code);
|
|
163
|
+
}
|
|
164
|
+
flush();
|
|
165
|
+
return runs;
|
|
166
|
+
}
|
|
167
|
+
function splitEntriesByBoundaries(entries, boundaries) {
|
|
168
|
+
const groupCount = Math.max(boundaries.length - 1, 0);
|
|
169
|
+
const groups = Array.from({ length: groupCount }, () => []);
|
|
170
|
+
let index = 0;
|
|
171
|
+
const advancePastEmptyGroups = () => {
|
|
172
|
+
while (index < groupCount && boundaries[index] === boundaries[index + 1]) index += 1;
|
|
173
|
+
};
|
|
174
|
+
advancePastEmptyGroups();
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
const group = groups[index];
|
|
177
|
+
if (group === void 0) break;
|
|
178
|
+
group.push(entry);
|
|
179
|
+
if (entry.endCp === boundaries[index + 1]) {
|
|
180
|
+
index += 1;
|
|
181
|
+
advancePastEmptyGroups();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return groups;
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
export { readParagraphs, splitEntriesByBoundaries };
|
|
@@ -1,28 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
interface Piece {
|
|
3
|
-
/** The first character position this piece supplies, PlcPcd.aCp[i]. */
|
|
4
|
-
readonly cpStart: number;
|
|
5
|
-
/** One past the last character position this piece supplies, PlcPcd.aCp[i + 1]. */
|
|
6
|
-
readonly cpEnd: number;
|
|
7
|
-
/** FcCompressed's 30-bit offset field as stored -- NOT yet halved for a compressed piece. Use characterOffset() rather than this directly. */
|
|
8
|
-
readonly fc: number;
|
|
9
|
-
/** True when the piece's characters occupy one byte each and its real byte offset is `fc / 2`. */
|
|
10
|
-
readonly compressed: boolean;
|
|
11
|
-
/** Pcd's fNoParaLast: "If this bit is 1, the text MUST NOT contain a paragraph mark." */
|
|
12
|
-
readonly noParaLast: boolean;
|
|
13
|
-
/** Pcd.Prm, [MS-DOC] 2.8.36 -- further property modifications for this piece's text, carried verbatim and not yet applied (see README's scope note). */
|
|
14
|
-
readonly prm: number;
|
|
15
|
-
}
|
|
16
|
-
interface PieceTable {
|
|
17
|
-
readonly pieces: readonly Piece[];
|
|
18
|
-
/** PlcPcd.aCp itself: one more entry than there are pieces, so a lookup can bracket every piece and terminate at the document's end. */
|
|
19
|
-
readonly cpKeys: readonly number[];
|
|
20
|
-
/** The final aCp entry, one past the last character position the document defines. */
|
|
21
|
-
readonly lastCp: number;
|
|
22
|
-
}
|
|
23
|
-
declare function parseClx(clx: Uint8Array): PieceTable;
|
|
24
|
-
declare function characterOffset(piece: Piece, cp: number): number;
|
|
25
|
-
/** The number of bytes one character occupies in this piece: one for a compressed (8-bit) piece, two for an uncompressed (16-bit) one. */
|
|
26
|
-
declare function characterSize(piece: Piece): 1 | 2;
|
|
27
|
-
//#endregion
|
|
1
|
+
import { a as parseClx, i as characterSize, n as PieceTable, r as characterOffset, t as Piece } from "../piece-table-CA18mhjv.cjs";
|
|
28
2
|
export { Piece, PieceTable, characterOffset, characterSize, parseClx };
|
|
@@ -1,28 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
interface Piece {
|
|
3
|
-
/** The first character position this piece supplies, PlcPcd.aCp[i]. */
|
|
4
|
-
readonly cpStart: number;
|
|
5
|
-
/** One past the last character position this piece supplies, PlcPcd.aCp[i + 1]. */
|
|
6
|
-
readonly cpEnd: number;
|
|
7
|
-
/** FcCompressed's 30-bit offset field as stored -- NOT yet halved for a compressed piece. Use characterOffset() rather than this directly. */
|
|
8
|
-
readonly fc: number;
|
|
9
|
-
/** True when the piece's characters occupy one byte each and its real byte offset is `fc / 2`. */
|
|
10
|
-
readonly compressed: boolean;
|
|
11
|
-
/** Pcd's fNoParaLast: "If this bit is 1, the text MUST NOT contain a paragraph mark." */
|
|
12
|
-
readonly noParaLast: boolean;
|
|
13
|
-
/** Pcd.Prm, [MS-DOC] 2.8.36 -- further property modifications for this piece's text, carried verbatim and not yet applied (see README's scope note). */
|
|
14
|
-
readonly prm: number;
|
|
15
|
-
}
|
|
16
|
-
interface PieceTable {
|
|
17
|
-
readonly pieces: readonly Piece[];
|
|
18
|
-
/** PlcPcd.aCp itself: one more entry than there are pieces, so a lookup can bracket every piece and terminate at the document's end. */
|
|
19
|
-
readonly cpKeys: readonly number[];
|
|
20
|
-
/** The final aCp entry, one past the last character position the document defines. */
|
|
21
|
-
readonly lastCp: number;
|
|
22
|
-
}
|
|
23
|
-
declare function parseClx(clx: Uint8Array): PieceTable;
|
|
24
|
-
declare function characterOffset(piece: Piece, cp: number): number;
|
|
25
|
-
/** The number of bytes one character occupies in this piece: one for a compressed (8-bit) piece, two for an uncompressed (16-bit) one. */
|
|
26
|
-
declare function characterSize(piece: Piece): 1 | 2;
|
|
27
|
-
//#endregion
|
|
1
|
+
import { a as parseClx, i as characterSize, n as PieceTable, r as characterOffset, t as Piece } from "../piece-table-CA18mhjv.js";
|
|
28
2
|
export { Piece, PieceTable, characterOffset, characterSize, parseClx };
|
package/dist/write.cjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_errors = require("./errors.cjs");
|
|
3
3
|
const require_detect = require("./detect.cjs");
|
|
4
|
+
require("./prop/fkp.cjs");
|
|
5
|
+
const require_style_stsh = require("./style/stsh.cjs");
|
|
6
|
+
require("./text/special.cjs");
|
|
4
7
|
const require_fib_write = require("./fib/write.cjs");
|
|
5
8
|
const require_metadata = require("./metadata.cjs");
|
|
6
9
|
const require_text_piece_table_write = require("./text/piece-table-write.cjs");
|
|
7
|
-
require("./text/special.cjs");
|
|
8
|
-
require("./prop/fkp.cjs");
|
|
9
10
|
const require_prop_fkp_write = require("./prop/fkp-write.cjs");
|
|
10
11
|
const require_prop_chp_write = require("./prop/chp-write.cjs");
|
|
11
12
|
const require_prop_pap_write = require("./prop/pap-write.cjs");
|
|
12
13
|
const require_prop_sep_write = require("./prop/sep-write.cjs");
|
|
13
|
-
const require_style_stsh = require("./style/stsh.cjs");
|
|
14
14
|
const require_style_fonts = require("./style/fonts.cjs");
|
|
15
15
|
const require_list_numbering_write = require("./list/numbering-write.cjs");
|
|
16
16
|
const require_table_write = require("./table/write.cjs");
|
package/dist/write.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { DocFormatError, DocUnsupportedError } from "./errors.js";
|
|
2
2
|
import { SUMMARY_INFORMATION_STREAM, WORD_DOCUMENT_STREAM } from "./detect.js";
|
|
3
|
+
import "./prop/fkp.js";
|
|
4
|
+
import { buildStshForStyles } from "./style/stsh.js";
|
|
5
|
+
import "./text/special.js";
|
|
3
6
|
import { buildFib } from "./fib/write.js";
|
|
4
7
|
import { layoutMetadataToSummaryInformation as layoutMetadataToSummaryInformation$1 } from "./metadata.js";
|
|
5
8
|
import { buildTextClx } from "./text/piece-table-write.js";
|
|
6
|
-
import "./text/special.js";
|
|
7
|
-
import "./prop/fkp.js";
|
|
8
9
|
import { buildChpxPages, buildPapxPages, buildPropertyBinTable, firstFcOfPage } from "./prop/fkp-write.js";
|
|
9
10
|
import { encodeCharacterGrpprl } from "./prop/chp-write.js";
|
|
10
11
|
import { encodeParagraphGrpprl } from "./prop/pap-write.js";
|
|
11
12
|
import { buildPlcfSed, buildSepx, encodeSectionGrpprl } from "./prop/sep-write.js";
|
|
12
|
-
import { buildStshForStyles } from "./style/stsh.js";
|
|
13
13
|
import { buildFontTable } from "./style/fonts.js";
|
|
14
14
|
import { buildNumberingTables, gatherListUsage } from "./list/numbering-write.js";
|
|
15
15
|
import { flattenSectionBlocks } from "./table/write.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doc-codec",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "A hand-written reader for the Word Binary File Format ([MS-DOC], .doc) against the shared document-schema.js content pivot: FIB parsing, piece-table text reconstruction, and CHPX/PAPX formatting exceptions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"license": "MIT",
|
|
79
79
|
"packageManager": "pnpm@11.6.0",
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"archive-codec": "1.
|
|
81
|
+
"archive-codec": "1.10.0",
|
|
82
82
|
"document-schema.js": "7.5.0"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { t as Fib } from "./fib-DWFta19r.cjs";
|
|
2
|
-
//#region src/list/numbering.d.ts
|
|
3
|
-
/** MSONFC ([MS-OSHARED] 2.2.1.3), mapped to its own documented ST_NumberFormat equivalent -- the identical vocabulary ooxml.js's NumberingLevel.format carries verbatim from word/numbering.xml's own w:numFmt/@w:val. Every member through msonfcUCRus (0x3B) is a real numbered/lettered/ideograph format; 0x17 (msonfcBullet) is handled separately below since PlfLfo also treats it as the "no number sequence, but has bullets" case LVLF's own field text calls out by name. */
|
|
4
|
-
declare const NUMBER_FORMAT_BY_NFC: Readonly<Record<number, string>>;
|
|
5
|
-
interface NumberingLevel {
|
|
6
|
-
/** The ST_NumberFormat-equivalent string MSONFC's own value maps to ("decimal", "upperRoman", "bullet", ...), or "none" for a level with no number sequence at all ([MS-DOC] 2.9.150's own nfc field text: "If this is equal to 0xFF..., this level does not have a number sequence"). */
|
|
7
|
-
readonly format: string;
|
|
8
|
-
/** The level's own text template: a placeholder pattern like '%1.' or '%2)' for a numbered format (the digit names which zero-based level's own counter substitutes at that position, one-based in the placeholder itself) -- the identical convention ooxml.js's own NumberingLevel.text carries verbatim from w:lvlText/@w:val -- or a literal bullet glyph string for format 'bullet'. Decoded from the level's own Xst (a raw UTF-16 string) plus its rgbxchNums array, which names which character POSITIONS in that string are placeholders rather than literal text -- see readLevelText below. */
|
|
9
|
-
readonly text: string;
|
|
10
|
-
/** iStartAt: the value this level's counter begins from. Meaningless (and not read as anything but 1) for a level with no number sequence. */
|
|
11
|
-
readonly startAt: number;
|
|
12
|
-
/** ilvlRestartLim ([MS-DOC] 2.9.150), only when fNoRestart is set: the first (most-significant) zero-based level after which this level's own number sequence does NOT restart. Absent (undefined) is the spec's own default behaviour -- "restarts when a more significant level is encountered" -- not "never restarts". */
|
|
13
|
-
readonly restart?: number;
|
|
14
|
-
}
|
|
15
|
-
interface NumberingDefinition {
|
|
16
|
-
/** Keyed by the level's own zero-based ilvl, stringified -- the identical zero-based numbering ContentListMembership.level already uses, so `definitions[membership.numId]?.levels[String(membership.level)]` is the direct lookup path from a paragraph's own membership to its rendering definition. A record rather than a fixed-length array/tuple: a simple (fSimpleList) LSTF states only level 0. */
|
|
17
|
-
readonly levels: Readonly<Record<string, NumberingLevel>>;
|
|
18
|
-
}
|
|
19
|
-
/** Keyed by a paragraph's own listId (ContentListMembership.numId, stringified) -- prop/pap.ts's own sprmPIlfo, a one-based index into PlfLfo.rgLfo. */
|
|
20
|
-
type NumberingDefinitions = Readonly<Record<string, NumberingDefinition>>;
|
|
21
|
-
/** Resolves PlfLst and PlfLfo into NumberingDefinitions, keyed by the one-based ilfo every listId already is (prop/pap.ts's own Math.abs(ilfo)) -- absent entirely when the file carries neither (fcPlfLst/fcPlfLfo both 0, a document with no lists at all, the common case this reader must not fail on). */
|
|
22
|
-
declare function readNumberingDefinitions(table: Uint8Array, fib: Fib): NumberingDefinitions;
|
|
23
|
-
//#endregion
|
|
24
|
-
export { readNumberingDefinitions as a, NumberingLevel as i, NumberingDefinition as n, NumberingDefinitions as r, NUMBER_FORMAT_BY_NFC as t };
|