doc-codec 1.0.2 → 1.1.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 +192 -50
- package/dist/color.cjs +166 -0
- package/dist/color.d.cts +20 -0
- package/dist/color.d.ts +20 -0
- package/dist/color.js +160 -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/fib/fib.cjs +9 -1
- package/dist/fib/fib.d.cts +1 -28
- package/dist/fib/fib.d.ts +1 -28
- package/dist/fib/fib.js +9 -1
- package/dist/fib/offsets.cjs +9 -1
- package/dist/fib/offsets.d.cts +8 -0
- package/dist/fib/offsets.d.ts +8 -0
- package/dist/fib/offsets.js +9 -1
- package/dist/fib/write.cjs +43 -0
- package/dist/fib/write.d.cts +21 -0
- package/dist/fib/write.d.ts +21 -0
- package/dist/fib/write.js +42 -0
- package/dist/fib-CtsLtF96.d.cts +37 -0
- package/dist/fib-CtsLtF96.d.ts +37 -0
- package/dist/index.cjs +31 -0
- package/dist/index.d.cts +18 -7
- package/dist/index.d.ts +18 -7
- package/dist/index.js +14 -3
- package/dist/list/numbering.cjs +206 -0
- package/dist/list/numbering.d.cts +2 -0
- package/dist/list/numbering.d.ts +2 -0
- package/dist/list/numbering.js +205 -0
- package/dist/metadata.cjs +14 -0
- package/dist/metadata.d.cts +6 -0
- package/dist/metadata.d.ts +6 -0
- package/dist/metadata.js +13 -0
- package/dist/numbering-Cir-uGdc.d.cts +22 -0
- package/dist/numbering-DF1z1PyG.d.ts +22 -0
- package/dist/pap-BkFo8dZO.d.ts +29 -0
- package/dist/pap-BmelEYEP.d.cts +29 -0
- package/dist/prop/chp-write.cjs +47 -0
- package/dist/prop/chp-write.d.cts +5 -0
- package/dist/prop/chp-write.d.ts +5 -0
- package/dist/prop/chp-write.js +46 -0
- package/dist/prop/chp.cjs +15 -107
- package/dist/prop/chp.d.cts +3 -2
- package/dist/prop/chp.d.ts +3 -2
- package/dist/prop/chp.js +15 -107
- package/dist/prop/fkp-write.cjs +139 -0
- package/dist/prop/fkp-write.d.cts +21 -0
- package/dist/prop/fkp-write.d.ts +21 -0
- package/dist/prop/fkp-write.js +135 -0
- package/dist/prop/pap-write.cjs +67 -0
- package/dist/prop/pap-write.d.cts +5 -0
- package/dist/prop/pap-write.d.ts +5 -0
- package/dist/prop/pap-write.js +66 -0
- package/dist/prop/pap.cjs +10 -0
- package/dist/prop/pap.d.cts +1 -24
- package/dist/prop/pap.d.ts +1 -24
- package/dist/prop/pap.js +11 -1
- package/dist/prop/sep-write.cjs +63 -0
- package/dist/prop/sep-write.d.cts +9 -0
- package/dist/prop/sep-write.d.ts +9 -0
- package/dist/prop/sep-write.js +60 -0
- package/dist/prop/sep.cjs +63 -0
- package/dist/prop/sep.d.cts +16 -0
- package/dist/prop/sep.d.ts +16 -0
- package/dist/prop/sep.js +61 -0
- package/dist/prop/sprm.d.cts +1 -25
- package/dist/prop/sprm.d.ts +1 -25
- package/dist/read.cjs +44 -18
- package/dist/read.d.cts +21 -4
- package/dist/read.d.ts +21 -4
- package/dist/read.js +46 -20
- package/dist/sprm-Djg5RNiq.d.cts +26 -0
- package/dist/sprm-Djg5RNiq.d.ts +26 -0
- package/dist/style/fonts.cjs +68 -0
- package/dist/style/fonts.d.cts +5 -0
- package/dist/style/fonts.d.ts +5 -0
- package/dist/style/fonts.js +66 -0
- package/dist/style/stsh.cjs +23 -0
- package/dist/style/stsh.d.cts +2 -1
- package/dist/style/stsh.d.ts +2 -1
- package/dist/style/stsh.js +23 -1
- package/dist/table/decoration.cjs +220 -0
- package/dist/table/decoration.d.cts +41 -0
- package/dist/table/decoration.d.ts +41 -0
- package/dist/table/decoration.js +205 -0
- package/dist/table/read.cjs +192 -0
- package/dist/table/read.d.cts +6 -0
- package/dist/table/read.d.ts +6 -0
- package/dist/table/read.js +191 -0
- package/dist/table/tap-write.cjs +125 -0
- package/dist/table/tap-write.d.cts +13 -0
- package/dist/table/tap-write.d.ts +13 -0
- package/dist/table/tap-write.js +124 -0
- package/dist/table/tap.cjs +217 -0
- package/dist/table/tap.d.cts +29 -0
- package/dist/table/tap.d.ts +29 -0
- package/dist/table/tap.js +213 -0
- package/dist/table/write.cjs +136 -0
- package/dist/table/write.d.cts +13 -0
- package/dist/table/write.d.ts +13 -0
- package/dist/table/write.js +135 -0
- package/dist/text/piece-table-write.cjs +24 -0
- package/dist/text/piece-table-write.d.cts +4 -0
- package/dist/text/piece-table-write.d.ts +4 -0
- package/dist/text/piece-table-write.js +23 -0
- package/dist/write.cjs +175 -0
- package/dist/write.d.cts +5 -0
- package/dist/write.d.ts +5 -0
- package/dist/write.js +174 -0
- package/package.json +3 -3
package/dist/read.d.ts
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
|
-
import { Fib } from "./fib
|
|
2
|
-
import {
|
|
1
|
+
import { t as Fib } from "./fib-CtsLtF96.js";
|
|
2
|
+
import { t as Prl } from "./sprm-Djg5RNiq.js";
|
|
3
|
+
import { t as ParagraphProperties } from "./pap-BkFo8dZO.js";
|
|
4
|
+
import { n as NumberingDefinitions } from "./numbering-DF1z1PyG.js";
|
|
5
|
+
import { ContentDocument, ContentParagraph } from "document-schema.js";
|
|
3
6
|
//#region src/read.d.ts
|
|
4
7
|
interface DocStreams {
|
|
5
8
|
readonly wordDocument: Uint8Array;
|
|
6
9
|
readonly table: Uint8Array;
|
|
7
10
|
readonly fib: Fib;
|
|
11
|
+
/** The raw "\x05SummaryInformation" stream bytes, or undefined when the container carries none -- a valid, spec-conformant Word Binary File need not carry document properties at all. */
|
|
12
|
+
readonly metadata: Uint8Array<ArrayBuffer> | undefined;
|
|
8
13
|
}
|
|
9
14
|
declare function readDocStreams(bytes: Uint8Array<ArrayBuffer>): DocStreams;
|
|
10
|
-
|
|
15
|
+
/** readDocContent's own return type: a ContentDocument (kind 'wordprocessing') plus numbering -- the list-level formatting (glyph/format, level-text template, start-at value) PlfLst/PlfLfo carry, which ContentListMembership has nowhere to hold. Mirrors ooxml.js's own DocxDocument.numbering exactly in field name and NumberingDefinitions' own shape (see list/numbering.ts's top comment for why it sits outside the shared schema rather than inside ContentListMembership); unlike DocxDocument, DocContent stays a genuine ContentDocument subtype (an intersection, not a fresh shape) since readDocContent already had one return type to widen rather than two to reconcile. */
|
|
16
|
+
type DocContent = ContentDocument & {
|
|
17
|
+
readonly numbering: NumberingDefinitions;
|
|
18
|
+
};
|
|
19
|
+
declare function readDocContent(bytes: Uint8Array<ArrayBuffer>): DocContent;
|
|
20
|
+
/** One paragraph/cell/row-ending mark, still flat -- table/read.ts's assembleBlocks is what folds a run of these into a real ContentTable. `properties` and `grpprl` are carried alongside the already-built `paragraph` because table grouping needs sprmPFInTable/sprmPFTtp/sprmPItap (properties) and, on a row's own mark, its table-defining sgc-5 sprms (grpprl) -- neither of which survives onto a plain ContentParagraph. */
|
|
21
|
+
interface ParagraphEntry {
|
|
22
|
+
readonly paragraph: ContentParagraph;
|
|
23
|
+
readonly properties: ParagraphProperties;
|
|
24
|
+
readonly grpprl: readonly Prl[];
|
|
25
|
+
/** The character that terminated this paragraph in the text stream: PARAGRAPH_MARK, CELL_MARK, or SECTION_MARK. */
|
|
26
|
+
readonly terminator: number;
|
|
27
|
+
}
|
|
11
28
|
//#endregion
|
|
12
|
-
export { DocStreams, readDocContent, readDocStreams };
|
|
29
|
+
export { DocContent, DocStreams, ParagraphEntry, readDocContent, readDocStreams };
|
package/dist/read.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DocFormatError } from "./errors.js";
|
|
2
2
|
import { slice } from "./bytes.js";
|
|
3
|
-
import { WORD_DOCUMENT_STREAM } from "./detect.js";
|
|
3
|
+
import { SUMMARY_INFORMATION_STREAM, WORD_DOCUMENT_STREAM } from "./detect.js";
|
|
4
4
|
import { parseFib, tableStreamName } from "./fib/fib.js";
|
|
5
5
|
import { parseClx } from "./text/piece-table.js";
|
|
6
6
|
import { readTextRange } from "./text/characters.js";
|
|
@@ -9,10 +9,14 @@ import { readGrpprl } from "./prop/sprm.js";
|
|
|
9
9
|
import { PropertyBinTable } from "./prop/fkp.js";
|
|
10
10
|
import { applyCharacterSprms } from "./prop/chp.js";
|
|
11
11
|
import { applyParagraphSprms } from "./prop/pap.js";
|
|
12
|
+
import { readSectionProperties } from "./prop/sep.js";
|
|
12
13
|
import { headingLevelFromIstd, parseStsh } from "./style/stsh.js";
|
|
13
|
-
import {
|
|
14
|
+
import { parseFontTable } from "./style/fonts.js";
|
|
15
|
+
import { readNumberingDefinitions } from "./list/numbering.js";
|
|
16
|
+
import { assembleBlocks } from "./table/read.js";
|
|
17
|
+
import { readCompoundFile, readSummaryInformation, summaryInformationToLayoutMetadata } from "archive-codec";
|
|
14
18
|
//#region src/read.ts
|
|
15
|
-
/**
|
|
19
|
+
/** Word's own default for a new document (US Letter, one-inch margins) -- what a field this reader resolves from PlcfSed/Sepx (prop/sep.ts's readSectionProperties) falls back to when the file states nothing for it, exactly as it would fall back to Word's own implementation-dependent default for that one unstated sprm. */
|
|
16
20
|
const DEFAULT_PAGE_SIZE = {
|
|
17
21
|
widthPt: 612,
|
|
18
22
|
heightPt: 792
|
|
@@ -31,64 +35,86 @@ function readDocStreams(bytes) {
|
|
|
31
35
|
const wanted = tableStreamName(fib);
|
|
32
36
|
const table = streams.find((stream) => stream.path === wanted);
|
|
33
37
|
if (table === void 0) throw new DocFormatError(`FibBase.fWhichTblStm selects the "${wanted}" stream, which this compound file does not contain`);
|
|
38
|
+
const metadata = streams.find((stream) => stream.path === SUMMARY_INFORMATION_STREAM);
|
|
34
39
|
return {
|
|
35
40
|
wordDocument: wordDocument.bytes,
|
|
36
41
|
table: table.bytes,
|
|
37
|
-
fib
|
|
42
|
+
fib,
|
|
43
|
+
metadata: metadata?.bytes
|
|
38
44
|
};
|
|
39
45
|
}
|
|
40
46
|
function readDocContent(bytes) {
|
|
41
|
-
const { wordDocument, table, fib } = readDocStreams(bytes);
|
|
47
|
+
const { wordDocument, table, fib, metadata } = readDocStreams(bytes);
|
|
42
48
|
const pieceTable = parseClx(slice(table, fib.fcClx, fib.lcbClx, "Clx in the Table stream"));
|
|
43
49
|
const styles = fib.lcbStshf > 0 ? parseStsh(slice(table, fib.fcStshf, fib.lcbStshf, "STSH in the Table stream")) : void 0;
|
|
44
50
|
const chpxTable = new PropertyBinTable(wordDocument, slice(table, fib.fcPlcfBteChpx, fib.lcbPlcfBteChpx, "PlcBteChpx in the Table stream"), "PlcBteChpx");
|
|
45
51
|
const papxTable = new PropertyBinTable(wordDocument, slice(table, fib.fcPlcfBtePapx, fib.lcbPlcfBtePapx, "PlcBtePapx in the Table stream"), "PlcBtePapx");
|
|
52
|
+
const fonts = fib.lcbSttbfFfn > 0 ? parseFontTable(slice(table, fib.fcSttbfFfn, fib.lcbSttbfFfn, "SttbfFfn in the Table stream")) : void 0;
|
|
46
53
|
const range = readTextRange(wordDocument, pieceTable, 0, fib.ccpText);
|
|
47
|
-
const
|
|
54
|
+
const entries = readParagraphs(range.text, range.fcs, {
|
|
48
55
|
chpxTable,
|
|
49
56
|
papxTable,
|
|
50
57
|
styles,
|
|
58
|
+
fonts,
|
|
51
59
|
characterProperties: /* @__PURE__ */ new Map()
|
|
52
60
|
});
|
|
61
|
+
const blocks = assembleBlocks(entries);
|
|
62
|
+
const numbering = readNumberingDefinitions(table, fib);
|
|
63
|
+
const sectionProperties = readSectionProperties(wordDocument, table, fib);
|
|
53
64
|
return {
|
|
54
65
|
kind: "wordprocessing",
|
|
55
|
-
metadata: {},
|
|
66
|
+
metadata: metadata === void 0 ? {} : summaryInformationToLayoutMetadata(readSummaryInformation(metadata)),
|
|
56
67
|
sections: [{
|
|
57
|
-
pageSize:
|
|
58
|
-
|
|
68
|
+
pageSize: {
|
|
69
|
+
widthPt: sectionProperties.pageWidthPt ?? DEFAULT_PAGE_SIZE.widthPt,
|
|
70
|
+
heightPt: sectionProperties.pageHeightPt ?? DEFAULT_PAGE_SIZE.heightPt
|
|
71
|
+
},
|
|
72
|
+
margins: {
|
|
73
|
+
leftPt: sectionProperties.marginLeftPt ?? DEFAULT_MARGINS.leftPt,
|
|
74
|
+
rightPt: sectionProperties.marginRightPt ?? DEFAULT_MARGINS.rightPt,
|
|
75
|
+
topPt: sectionProperties.marginTopPt ?? DEFAULT_MARGINS.topPt,
|
|
76
|
+
bottomPt: sectionProperties.marginBottomPt ?? DEFAULT_MARGINS.bottomPt
|
|
77
|
+
},
|
|
59
78
|
blocks
|
|
60
|
-
}]
|
|
79
|
+
}],
|
|
80
|
+
numbering
|
|
61
81
|
};
|
|
62
82
|
}
|
|
63
83
|
function readParagraphs(text, fcs, context) {
|
|
64
|
-
const
|
|
84
|
+
const entries = [];
|
|
65
85
|
let start = 0;
|
|
66
86
|
for (let index = 0; index < text.length; index += 1) {
|
|
67
87
|
const code = text.charCodeAt(index);
|
|
68
88
|
if (!endsParagraph(code)) continue;
|
|
69
89
|
const markFc = fcs[index];
|
|
70
90
|
if (markFc === void 0) throw new DocFormatError(`character ${index} has no byte offset, so its paragraph's properties cannot be located`);
|
|
71
|
-
|
|
91
|
+
entries.push(buildParagraph(text.slice(start, index), fcs.slice(start, index), markFc, code, context));
|
|
72
92
|
start = index + 1;
|
|
73
93
|
}
|
|
74
94
|
if (start < text.length) {
|
|
75
95
|
const firstFc = fcs[start];
|
|
76
96
|
if (firstFc === void 0) throw new DocFormatError(`character ${start} has no byte offset, so the trailing paragraph's properties cannot be located`);
|
|
77
|
-
|
|
97
|
+
entries.push(buildParagraph(text.slice(start), fcs.slice(start), firstFc, 13, context));
|
|
78
98
|
}
|
|
79
|
-
return
|
|
99
|
+
return entries;
|
|
80
100
|
}
|
|
81
|
-
function buildParagraph(text, fcs, propertyFc, context) {
|
|
101
|
+
function buildParagraph(text, fcs, propertyFc, terminator, context) {
|
|
82
102
|
const papx = context.papxTable.papx(propertyFc);
|
|
83
103
|
const properties = {};
|
|
104
|
+
const grpprl = papx !== void 0 ? readGrpprl(papx.grpprl) : [];
|
|
84
105
|
if (papx !== void 0) {
|
|
85
106
|
properties.istd = papx.istd;
|
|
86
|
-
applyParagraphSprms(
|
|
107
|
+
applyParagraphSprms(grpprl, properties);
|
|
87
108
|
}
|
|
88
109
|
return {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
110
|
+
paragraph: {
|
|
111
|
+
kind: "paragraph",
|
|
112
|
+
runs: buildRuns(text, fcs, context),
|
|
113
|
+
...paragraphAttributes(properties, context)
|
|
114
|
+
},
|
|
115
|
+
properties,
|
|
116
|
+
grpprl,
|
|
117
|
+
terminator
|
|
92
118
|
};
|
|
93
119
|
}
|
|
94
120
|
function paragraphAttributes(properties, context) {
|
|
@@ -155,7 +181,7 @@ function buildRuns(text, fcs, context) {
|
|
|
155
181
|
let properties = context.characterProperties.get(key);
|
|
156
182
|
if (properties === void 0) {
|
|
157
183
|
properties = {};
|
|
158
|
-
if (grpprl !== void 0) applyCharacterSprms(readGrpprl(grpprl), properties);
|
|
184
|
+
if (grpprl !== void 0) applyCharacterSprms(readGrpprl(grpprl), properties, context.fonts);
|
|
159
185
|
context.characterProperties.set(key, properties);
|
|
160
186
|
}
|
|
161
187
|
currentProperties = properties;
|
|
@@ -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 { operandSize as a, decodeSprm as i, SGC as n, readGrpprl as o, Sprm as r, Prl as t };
|
|
@@ -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 { operandSize as a, decodeSprm as i, SGC as n, readGrpprl as o, Sprm as r, Prl as t };
|
|
@@ -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/style/fonts.ts
|
|
5
|
+
/** FFN's fixed head before xszFfn: ffid(1) + wWeight(2) + chs(1) + ixchSzAlt(1) + panose(10) + fs(24). */
|
|
6
|
+
const FFN_FIXED_SIZE = 39;
|
|
7
|
+
/** chs's ANSI_CHARSET, [MS-DOC] 2.9.87's own value 0 -- the character set every font this package writes is declared to use, since the document's own text is written as Unicode regardless (see text/piece-table-write.ts). */
|
|
8
|
+
const FFN_CHARSET_ANSI = 0;
|
|
9
|
+
/** A non-extended STTB's own per-entry length prefix is one byte, so one FFN record (fixed head plus a null-terminated name, each name character and terminator being 2 bytes) cannot exceed this. */
|
|
10
|
+
const MAX_ENTRY_BYTES = 255;
|
|
11
|
+
function parseFontTable(sttbfFfn) {
|
|
12
|
+
const cData = require_bytes.readUint16LE(sttbfFfn, 0);
|
|
13
|
+
const cbExtra = require_bytes.readUint16LE(sttbfFfn, 2);
|
|
14
|
+
if (cbExtra !== 0) throw new require_errors.DocFormatError(`SttbfFfn.cbExtra is ${cbExtra}, but [MS-DOC] 2.9.253 requires it to be 0`);
|
|
15
|
+
const names = [];
|
|
16
|
+
let cursor = 4;
|
|
17
|
+
for (let index = 0; index < cData; index += 1) {
|
|
18
|
+
const cch = require_bytes.readUint8(sttbfFfn, cursor);
|
|
19
|
+
const record = require_bytes.slice(sttbfFfn, cursor + 1, cch, `SttbfFfn entry ${index}`);
|
|
20
|
+
names.push(readFfnName(record, index));
|
|
21
|
+
cursor += 1 + cch;
|
|
22
|
+
}
|
|
23
|
+
return names;
|
|
24
|
+
}
|
|
25
|
+
function readFfnName(record, index) {
|
|
26
|
+
if (record.length < 41) throw new require_errors.DocFormatError(`FFN record ${index} is ${record.length} bytes, shorter than the fixed ${FFN_FIXED_SIZE}-byte head plus xszFfn's own null terminator`);
|
|
27
|
+
let name = "";
|
|
28
|
+
for (let offset = FFN_FIXED_SIZE; offset + 2 <= record.length; offset += 2) {
|
|
29
|
+
const unit = require_bytes.readUint16LE(record, offset);
|
|
30
|
+
if (unit === 0) return name;
|
|
31
|
+
name += String.fromCharCode(unit);
|
|
32
|
+
}
|
|
33
|
+
throw new require_errors.DocFormatError(`FFN record ${index}'s xszFfn runs to the end of the record with no null terminator`);
|
|
34
|
+
}
|
|
35
|
+
function buildFontTable(names) {
|
|
36
|
+
const bytes = [];
|
|
37
|
+
const push16 = (value) => {
|
|
38
|
+
bytes.push(value & 255, value >> 8 & 255);
|
|
39
|
+
};
|
|
40
|
+
push16(names.length);
|
|
41
|
+
push16(0);
|
|
42
|
+
for (const name of names) {
|
|
43
|
+
const record = buildFfnRecord(name);
|
|
44
|
+
if (record.length > MAX_ENTRY_BYTES) throw new require_errors.DocFormatError(`font name ${JSON.stringify(name)} produces a ${record.length}-byte FFN record, past the ${MAX_ENTRY_BYTES}-byte limit a non-extended STTB's one-byte cch can address`);
|
|
45
|
+
bytes.push(record.length, ...record);
|
|
46
|
+
}
|
|
47
|
+
return new Uint8Array(bytes);
|
|
48
|
+
}
|
|
49
|
+
function buildFfnRecord(name) {
|
|
50
|
+
const record = [
|
|
51
|
+
0,
|
|
52
|
+
144,
|
|
53
|
+
1,
|
|
54
|
+
FFN_CHARSET_ANSI,
|
|
55
|
+
0,
|
|
56
|
+
...new Array(10).fill(0),
|
|
57
|
+
...new Array(24).fill(0)
|
|
58
|
+
];
|
|
59
|
+
for (const character of name) {
|
|
60
|
+
const code = character.charCodeAt(0);
|
|
61
|
+
record.push(code & 255, code >> 8 & 255);
|
|
62
|
+
}
|
|
63
|
+
record.push(0, 0);
|
|
64
|
+
return record;
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
exports.buildFontTable = buildFontTable;
|
|
68
|
+
exports.parseFontTable = parseFontTable;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { DocFormatError } from "../errors.js";
|
|
2
|
+
import { readUint16LE, readUint8, slice } from "../bytes.js";
|
|
3
|
+
//#region src/style/fonts.ts
|
|
4
|
+
/** FFN's fixed head before xszFfn: ffid(1) + wWeight(2) + chs(1) + ixchSzAlt(1) + panose(10) + fs(24). */
|
|
5
|
+
const FFN_FIXED_SIZE = 39;
|
|
6
|
+
/** chs's ANSI_CHARSET, [MS-DOC] 2.9.87's own value 0 -- the character set every font this package writes is declared to use, since the document's own text is written as Unicode regardless (see text/piece-table-write.ts). */
|
|
7
|
+
const FFN_CHARSET_ANSI = 0;
|
|
8
|
+
/** A non-extended STTB's own per-entry length prefix is one byte, so one FFN record (fixed head plus a null-terminated name, each name character and terminator being 2 bytes) cannot exceed this. */
|
|
9
|
+
const MAX_ENTRY_BYTES = 255;
|
|
10
|
+
function parseFontTable(sttbfFfn) {
|
|
11
|
+
const cData = readUint16LE(sttbfFfn, 0);
|
|
12
|
+
const cbExtra = readUint16LE(sttbfFfn, 2);
|
|
13
|
+
if (cbExtra !== 0) throw new DocFormatError(`SttbfFfn.cbExtra is ${cbExtra}, but [MS-DOC] 2.9.253 requires it to be 0`);
|
|
14
|
+
const names = [];
|
|
15
|
+
let cursor = 4;
|
|
16
|
+
for (let index = 0; index < cData; index += 1) {
|
|
17
|
+
const cch = readUint8(sttbfFfn, cursor);
|
|
18
|
+
const record = slice(sttbfFfn, cursor + 1, cch, `SttbfFfn entry ${index}`);
|
|
19
|
+
names.push(readFfnName(record, index));
|
|
20
|
+
cursor += 1 + cch;
|
|
21
|
+
}
|
|
22
|
+
return names;
|
|
23
|
+
}
|
|
24
|
+
function readFfnName(record, index) {
|
|
25
|
+
if (record.length < 41) throw new DocFormatError(`FFN record ${index} is ${record.length} bytes, shorter than the fixed ${FFN_FIXED_SIZE}-byte head plus xszFfn's own null terminator`);
|
|
26
|
+
let name = "";
|
|
27
|
+
for (let offset = FFN_FIXED_SIZE; offset + 2 <= record.length; offset += 2) {
|
|
28
|
+
const unit = readUint16LE(record, offset);
|
|
29
|
+
if (unit === 0) return name;
|
|
30
|
+
name += String.fromCharCode(unit);
|
|
31
|
+
}
|
|
32
|
+
throw new DocFormatError(`FFN record ${index}'s xszFfn runs to the end of the record with no null terminator`);
|
|
33
|
+
}
|
|
34
|
+
function buildFontTable(names) {
|
|
35
|
+
const bytes = [];
|
|
36
|
+
const push16 = (value) => {
|
|
37
|
+
bytes.push(value & 255, value >> 8 & 255);
|
|
38
|
+
};
|
|
39
|
+
push16(names.length);
|
|
40
|
+
push16(0);
|
|
41
|
+
for (const name of names) {
|
|
42
|
+
const record = buildFfnRecord(name);
|
|
43
|
+
if (record.length > MAX_ENTRY_BYTES) throw new DocFormatError(`font name ${JSON.stringify(name)} produces a ${record.length}-byte FFN record, past the ${MAX_ENTRY_BYTES}-byte limit a non-extended STTB's one-byte cch can address`);
|
|
44
|
+
bytes.push(record.length, ...record);
|
|
45
|
+
}
|
|
46
|
+
return new Uint8Array(bytes);
|
|
47
|
+
}
|
|
48
|
+
function buildFfnRecord(name) {
|
|
49
|
+
const record = [
|
|
50
|
+
0,
|
|
51
|
+
144,
|
|
52
|
+
1,
|
|
53
|
+
FFN_CHARSET_ANSI,
|
|
54
|
+
0,
|
|
55
|
+
...new Array(10).fill(0),
|
|
56
|
+
...new Array(24).fill(0)
|
|
57
|
+
];
|
|
58
|
+
for (const character of name) {
|
|
59
|
+
const code = character.charCodeAt(0);
|
|
60
|
+
record.push(code & 255, code >> 8 & 255);
|
|
61
|
+
}
|
|
62
|
+
record.push(0, 0);
|
|
63
|
+
return record;
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { buildFontTable, parseFontTable };
|
package/dist/style/stsh.cjs
CHANGED
|
@@ -58,8 +58,31 @@ function readXstz(bytes, offset, what) {
|
|
|
58
58
|
function headingLevelFromIstd(istd) {
|
|
59
59
|
return istd >= 1 && istd <= 9 ? istd : void 0;
|
|
60
60
|
}
|
|
61
|
+
function buildEmptyStsh() {
|
|
62
|
+
const stshi = [];
|
|
63
|
+
const push16 = (value) => {
|
|
64
|
+
stshi.push(value & 255, value >> 8 & 255);
|
|
65
|
+
};
|
|
66
|
+
push16(0);
|
|
67
|
+
push16(STDF_SIZE_WITHOUT_POST_2000);
|
|
68
|
+
push16(1);
|
|
69
|
+
push16(0);
|
|
70
|
+
push16(15);
|
|
71
|
+
push16(0);
|
|
72
|
+
push16(0);
|
|
73
|
+
push16(0);
|
|
74
|
+
push16(0);
|
|
75
|
+
push16(0);
|
|
76
|
+
push16(4);
|
|
77
|
+
return new Uint8Array([
|
|
78
|
+
stshi.length & 255,
|
|
79
|
+
stshi.length >> 8 & 255,
|
|
80
|
+
...stshi
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
61
83
|
//#endregion
|
|
62
84
|
exports.STI_USER_DEFINED = STI_USER_DEFINED;
|
|
63
85
|
exports.STK = STK;
|
|
86
|
+
exports.buildEmptyStsh = buildEmptyStsh;
|
|
64
87
|
exports.headingLevelFromIstd = headingLevelFromIstd;
|
|
65
88
|
exports.parseStsh = parseStsh;
|
package/dist/style/stsh.d.cts
CHANGED
|
@@ -25,5 +25,6 @@ interface StyleSheet {
|
|
|
25
25
|
}
|
|
26
26
|
declare function parseStsh(stsh: Uint8Array): StyleSheet;
|
|
27
27
|
declare function headingLevelFromIstd(istd: number): number | undefined;
|
|
28
|
+
declare function buildEmptyStsh(): Uint8Array;
|
|
28
29
|
//#endregion
|
|
29
|
-
export { STI_USER_DEFINED, STK, Style, StyleSheet, headingLevelFromIstd, parseStsh };
|
|
30
|
+
export { STI_USER_DEFINED, STK, Style, StyleSheet, buildEmptyStsh, headingLevelFromIstd, parseStsh };
|
package/dist/style/stsh.d.ts
CHANGED
|
@@ -25,5 +25,6 @@ interface StyleSheet {
|
|
|
25
25
|
}
|
|
26
26
|
declare function parseStsh(stsh: Uint8Array): StyleSheet;
|
|
27
27
|
declare function headingLevelFromIstd(istd: number): number | undefined;
|
|
28
|
+
declare function buildEmptyStsh(): Uint8Array;
|
|
28
29
|
//#endregion
|
|
29
|
-
export { STI_USER_DEFINED, STK, Style, StyleSheet, headingLevelFromIstd, parseStsh };
|
|
30
|
+
export { STI_USER_DEFINED, STK, Style, StyleSheet, buildEmptyStsh, headingLevelFromIstd, parseStsh };
|
package/dist/style/stsh.js
CHANGED
|
@@ -57,5 +57,27 @@ function readXstz(bytes, offset, what) {
|
|
|
57
57
|
function headingLevelFromIstd(istd) {
|
|
58
58
|
return istd >= 1 && istd <= 9 ? istd : void 0;
|
|
59
59
|
}
|
|
60
|
+
function buildEmptyStsh() {
|
|
61
|
+
const stshi = [];
|
|
62
|
+
const push16 = (value) => {
|
|
63
|
+
stshi.push(value & 255, value >> 8 & 255);
|
|
64
|
+
};
|
|
65
|
+
push16(0);
|
|
66
|
+
push16(STDF_SIZE_WITHOUT_POST_2000);
|
|
67
|
+
push16(1);
|
|
68
|
+
push16(0);
|
|
69
|
+
push16(15);
|
|
70
|
+
push16(0);
|
|
71
|
+
push16(0);
|
|
72
|
+
push16(0);
|
|
73
|
+
push16(0);
|
|
74
|
+
push16(0);
|
|
75
|
+
push16(4);
|
|
76
|
+
return new Uint8Array([
|
|
77
|
+
stshi.length & 255,
|
|
78
|
+
stshi.length >> 8 & 255,
|
|
79
|
+
...stshi
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
60
82
|
//#endregion
|
|
61
|
-
export { STI_USER_DEFINED, STK, headingLevelFromIstd, parseStsh };
|
|
83
|
+
export { STI_USER_DEFINED, STK, buildEmptyStsh, headingLevelFromIstd, parseStsh };
|