doc-codec 2.6.0 → 2.7.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.
@@ -0,0 +1,85 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_base64 = require("./base64.cjs");
3
+ const require_errors = require("./errors.cjs");
4
+ //#region src/pictures-write.ts
5
+ const PICF_SIZE = 68;
6
+ const PICF_MM_OFFSET = 6;
7
+ const PICF_DXA_GOAL_OFFSET = 28;
8
+ const PICF_DYA_GOAL_OFFSET = 30;
9
+ const PICF_MX_OFFSET = 32;
10
+ const PICF_MY_OFFSET = 34;
11
+ /** MFPF.mm's MM_SHAPE value, [MS-DOC] 2.9.181 -- the plain, no-source-filename form; this writer never emits MM_SHAPEFILE's own cchPicName/stPicName pair, matching what pictures.ts's own reader treats as the common case. */
12
+ const MM_SHAPE = 100;
13
+ const RECORD_HEADER_SIZE = 8;
14
+ /** OfficeArtBlipJPEG / OfficeArtBlipPNG record types, [MS-ODRAW] 2.2.27/2.2.28. */
15
+ const BLIP_JPEG = 61469;
16
+ const BLIP_PNG = 61470;
17
+ /** rh.recInstance for the single-rgbUid (16-byte) form of each blip -- [MS-ODRAW] 2.2.27's own table for JPEG (RGB), 2.2.28's for PNG; the identical values pictures.ts's own ONE_UID_INSTANCES set already recognises on read. */
18
+ const BLIP_INSTANCE_JPEG = 1130;
19
+ const BLIP_INSTANCE_PNG = 1760;
20
+ const BLIP_UID_SIZE = 16;
21
+ /** The one byte following rgbUid in every OfficeArtBlip variant pictures.ts reads -- [MS-ODRAW]'s own BLIPFileTag, 0xFF for a non-metafile blip (PNG/JPEG are never compressed the way a WMF/EMF metafile blip's own tag byte would state). */
22
+ const BLIP_FILE_TAG = 255;
23
+ const BLIP_TAG_SIZE = 1;
24
+ const TWIPS_PER_POINT = 20;
25
+ /** PICMID.mx/my, [MS-DOC]: "the ratio, measured in tenths of a percent, between the final display width/height and the initial picture width/height" -- this writer always states dxaGoal/dyaGoal as the image's own real size and mx/my as "no scaling" (1000, one thousand tenths-of-a-percent = 100%), matching pictures.ts's own read-side arithmetic (dxaGoal * mx / 1000) exactly at mx = 1000. */
26
+ const NO_SCALING = 1e3;
27
+ /** PICMID.dxaGoal/dyaGoal are a signed 16-bit FieldFormatting value in twips -- [MS-DOC] states no narrower bound than that field width itself. */
28
+ const MAX_INT16 = 32767;
29
+ function recordHeaderBytes(recType, recInstance, recLen) {
30
+ const bytes = new Uint8Array(RECORD_HEADER_SIZE);
31
+ const view = new DataView(bytes.buffer);
32
+ view.setUint16(0, recInstance << 4, true);
33
+ view.setUint16(2, recType, true);
34
+ view.setUint32(4, recLen, true);
35
+ return bytes;
36
+ }
37
+ function twipsFromPt(pt, field) {
38
+ const twips = Math.round(pt * TWIPS_PER_POINT);
39
+ if (twips < 0 || twips > MAX_INT16) throw new require_errors.DocFormatError(`an image's ${field} of ${String(pt)}pt is ${String(twips)} twips, outside the 0..${String(MAX_INT16)} range PICMID.dxaGoal/dyaGoal (a signed 16-bit field) can hold`);
40
+ return twips;
41
+ }
42
+ /** Builds one inline picture's own PICFAndOfficeArtData bytes -- everything pictures.ts's readInlinePicture needs given the Data-stream offset it will end up placed at, which this function does not itself decide (see WrittenInlinePicture's own comment). */
43
+ function buildInlinePicture(image) {
44
+ const recType = image.format === "png" ? BLIP_PNG : image.format === "jpeg" ? BLIP_JPEG : void 0;
45
+ if (recType === void 0) throw new require_errors.DocUnsupportedError(`doc-codec's writer can only write a 'png' or 'jpeg' inline picture -- the two raster formats its own reader decodes from a real OfficeArtBlip; got '${image.format}'`);
46
+ const recInstance = image.format === "png" ? BLIP_INSTANCE_PNG : BLIP_INSTANCE_JPEG;
47
+ const payload = require_base64.base64ToBytes(image.base64);
48
+ const picf = new Uint8Array(PICF_SIZE);
49
+ const picfView = new DataView(picf.buffer);
50
+ picfView.setUint16(PICF_MM_OFFSET, MM_SHAPE, true);
51
+ picfView.setInt16(PICF_DXA_GOAL_OFFSET, twipsFromPt(image.widthPt, "widthPt"), true);
52
+ picfView.setInt16(PICF_DYA_GOAL_OFFSET, twipsFromPt(image.heightPt, "heightPt"), true);
53
+ picfView.setUint16(PICF_MX_OFFSET, NO_SCALING, true);
54
+ picfView.setUint16(PICF_MY_OFFSET, NO_SCALING, true);
55
+ const shapeHeader = recordHeaderBytes(61444, 0, 0);
56
+ const uid = new Uint8Array(BLIP_UID_SIZE);
57
+ const blipHeader = recordHeaderBytes(recType, recInstance, uid.length + BLIP_TAG_SIZE + payload.length);
58
+ const data = new Uint8Array(picf.length + shapeHeader.length + blipHeader.length + uid.length + BLIP_TAG_SIZE + payload.length);
59
+ let cursor = 0;
60
+ data.set(picf, cursor);
61
+ cursor += picf.length;
62
+ data.set(shapeHeader, cursor);
63
+ cursor += shapeHeader.length;
64
+ data.set(blipHeader, cursor);
65
+ cursor += blipHeader.length;
66
+ data.set(uid, cursor);
67
+ cursor += uid.length;
68
+ data[cursor] = BLIP_FILE_TAG;
69
+ cursor += 1;
70
+ data.set(payload, cursor);
71
+ return {
72
+ data,
73
+ buildGrpprl: buildPicLocationGrpprl
74
+ };
75
+ }
76
+ /** sprmCPicLocation, [MS-DOC] 2.6.1 -- a signed 32-bit offset into the Data stream, little-endian. */
77
+ function buildPicLocationGrpprl(dataStreamOffset) {
78
+ const grpprl = [3, 106];
79
+ const operand = /* @__PURE__ */ new Uint8Array(4);
80
+ new DataView(operand.buffer).setInt32(0, dataStreamOffset, true);
81
+ grpprl.push(...operand);
82
+ return grpprl;
83
+ }
84
+ //#endregion
85
+ exports.buildInlinePicture = buildInlinePicture;
@@ -0,0 +1,12 @@
1
+ import { ContentImageBlock } from "document-schema.js";
2
+ //#region src/pictures-write.d.ts
3
+ interface WrittenInlinePicture {
4
+ /** The whole PICFAndOfficeArtData byte blob to append to the Data stream at whatever offset it ends up placed. */
5
+ readonly data: Uint8Array<ArrayBuffer>;
6
+ /** sprmCPicLocation's own grpprl bytes, complete except for its 4-byte operand, which the caller fills in with wherever `data` was actually placed (buildPicLocationGrpprl below) -- the two are split because only the caller (write.ts's own Data-stream accumulator) knows that offset before `data` is placed. */
7
+ readonly buildGrpprl: (dataStreamOffset: number) => number[];
8
+ }
9
+ /** Builds one inline picture's own PICFAndOfficeArtData bytes -- everything pictures.ts's readInlinePicture needs given the Data-stream offset it will end up placed at, which this function does not itself decide (see WrittenInlinePicture's own comment). */
10
+ declare function buildInlinePicture(image: ContentImageBlock): WrittenInlinePicture;
11
+ //#endregion
12
+ export { WrittenInlinePicture, buildInlinePicture };
@@ -0,0 +1,12 @@
1
+ import { ContentImageBlock } from "document-schema.js";
2
+ //#region src/pictures-write.d.ts
3
+ interface WrittenInlinePicture {
4
+ /** The whole PICFAndOfficeArtData byte blob to append to the Data stream at whatever offset it ends up placed. */
5
+ readonly data: Uint8Array<ArrayBuffer>;
6
+ /** sprmCPicLocation's own grpprl bytes, complete except for its 4-byte operand, which the caller fills in with wherever `data` was actually placed (buildPicLocationGrpprl below) -- the two are split because only the caller (write.ts's own Data-stream accumulator) knows that offset before `data` is placed. */
7
+ readonly buildGrpprl: (dataStreamOffset: number) => number[];
8
+ }
9
+ /** Builds one inline picture's own PICFAndOfficeArtData bytes -- everything pictures.ts's readInlinePicture needs given the Data-stream offset it will end up placed at, which this function does not itself decide (see WrittenInlinePicture's own comment). */
10
+ declare function buildInlinePicture(image: ContentImageBlock): WrittenInlinePicture;
11
+ //#endregion
12
+ export { WrittenInlinePicture, buildInlinePicture };
@@ -0,0 +1,84 @@
1
+ import { base64ToBytes } from "./base64.js";
2
+ import { DocFormatError, DocUnsupportedError } from "./errors.js";
3
+ //#region src/pictures-write.ts
4
+ const PICF_SIZE = 68;
5
+ const PICF_MM_OFFSET = 6;
6
+ const PICF_DXA_GOAL_OFFSET = 28;
7
+ const PICF_DYA_GOAL_OFFSET = 30;
8
+ const PICF_MX_OFFSET = 32;
9
+ const PICF_MY_OFFSET = 34;
10
+ /** MFPF.mm's MM_SHAPE value, [MS-DOC] 2.9.181 -- the plain, no-source-filename form; this writer never emits MM_SHAPEFILE's own cchPicName/stPicName pair, matching what pictures.ts's own reader treats as the common case. */
11
+ const MM_SHAPE = 100;
12
+ const RECORD_HEADER_SIZE = 8;
13
+ /** OfficeArtBlipJPEG / OfficeArtBlipPNG record types, [MS-ODRAW] 2.2.27/2.2.28. */
14
+ const BLIP_JPEG = 61469;
15
+ const BLIP_PNG = 61470;
16
+ /** rh.recInstance for the single-rgbUid (16-byte) form of each blip -- [MS-ODRAW] 2.2.27's own table for JPEG (RGB), 2.2.28's for PNG; the identical values pictures.ts's own ONE_UID_INSTANCES set already recognises on read. */
17
+ const BLIP_INSTANCE_JPEG = 1130;
18
+ const BLIP_INSTANCE_PNG = 1760;
19
+ const BLIP_UID_SIZE = 16;
20
+ /** The one byte following rgbUid in every OfficeArtBlip variant pictures.ts reads -- [MS-ODRAW]'s own BLIPFileTag, 0xFF for a non-metafile blip (PNG/JPEG are never compressed the way a WMF/EMF metafile blip's own tag byte would state). */
21
+ const BLIP_FILE_TAG = 255;
22
+ const BLIP_TAG_SIZE = 1;
23
+ const TWIPS_PER_POINT = 20;
24
+ /** PICMID.mx/my, [MS-DOC]: "the ratio, measured in tenths of a percent, between the final display width/height and the initial picture width/height" -- this writer always states dxaGoal/dyaGoal as the image's own real size and mx/my as "no scaling" (1000, one thousand tenths-of-a-percent = 100%), matching pictures.ts's own read-side arithmetic (dxaGoal * mx / 1000) exactly at mx = 1000. */
25
+ const NO_SCALING = 1e3;
26
+ /** PICMID.dxaGoal/dyaGoal are a signed 16-bit FieldFormatting value in twips -- [MS-DOC] states no narrower bound than that field width itself. */
27
+ const MAX_INT16 = 32767;
28
+ function recordHeaderBytes(recType, recInstance, recLen) {
29
+ const bytes = new Uint8Array(RECORD_HEADER_SIZE);
30
+ const view = new DataView(bytes.buffer);
31
+ view.setUint16(0, recInstance << 4, true);
32
+ view.setUint16(2, recType, true);
33
+ view.setUint32(4, recLen, true);
34
+ return bytes;
35
+ }
36
+ function twipsFromPt(pt, field) {
37
+ const twips = Math.round(pt * TWIPS_PER_POINT);
38
+ if (twips < 0 || twips > MAX_INT16) throw new DocFormatError(`an image's ${field} of ${String(pt)}pt is ${String(twips)} twips, outside the 0..${String(MAX_INT16)} range PICMID.dxaGoal/dyaGoal (a signed 16-bit field) can hold`);
39
+ return twips;
40
+ }
41
+ /** Builds one inline picture's own PICFAndOfficeArtData bytes -- everything pictures.ts's readInlinePicture needs given the Data-stream offset it will end up placed at, which this function does not itself decide (see WrittenInlinePicture's own comment). */
42
+ function buildInlinePicture(image) {
43
+ const recType = image.format === "png" ? BLIP_PNG : image.format === "jpeg" ? BLIP_JPEG : void 0;
44
+ if (recType === void 0) throw new DocUnsupportedError(`doc-codec's writer can only write a 'png' or 'jpeg' inline picture -- the two raster formats its own reader decodes from a real OfficeArtBlip; got '${image.format}'`);
45
+ const recInstance = image.format === "png" ? BLIP_INSTANCE_PNG : BLIP_INSTANCE_JPEG;
46
+ const payload = base64ToBytes(image.base64);
47
+ const picf = new Uint8Array(PICF_SIZE);
48
+ const picfView = new DataView(picf.buffer);
49
+ picfView.setUint16(PICF_MM_OFFSET, MM_SHAPE, true);
50
+ picfView.setInt16(PICF_DXA_GOAL_OFFSET, twipsFromPt(image.widthPt, "widthPt"), true);
51
+ picfView.setInt16(PICF_DYA_GOAL_OFFSET, twipsFromPt(image.heightPt, "heightPt"), true);
52
+ picfView.setUint16(PICF_MX_OFFSET, NO_SCALING, true);
53
+ picfView.setUint16(PICF_MY_OFFSET, NO_SCALING, true);
54
+ const shapeHeader = recordHeaderBytes(61444, 0, 0);
55
+ const uid = new Uint8Array(BLIP_UID_SIZE);
56
+ const blipHeader = recordHeaderBytes(recType, recInstance, uid.length + BLIP_TAG_SIZE + payload.length);
57
+ const data = new Uint8Array(picf.length + shapeHeader.length + blipHeader.length + uid.length + BLIP_TAG_SIZE + payload.length);
58
+ let cursor = 0;
59
+ data.set(picf, cursor);
60
+ cursor += picf.length;
61
+ data.set(shapeHeader, cursor);
62
+ cursor += shapeHeader.length;
63
+ data.set(blipHeader, cursor);
64
+ cursor += blipHeader.length;
65
+ data.set(uid, cursor);
66
+ cursor += uid.length;
67
+ data[cursor] = BLIP_FILE_TAG;
68
+ cursor += 1;
69
+ data.set(payload, cursor);
70
+ return {
71
+ data,
72
+ buildGrpprl: buildPicLocationGrpprl
73
+ };
74
+ }
75
+ /** sprmCPicLocation, [MS-DOC] 2.6.1 -- a signed 32-bit offset into the Data stream, little-endian. */
76
+ function buildPicLocationGrpprl(dataStreamOffset) {
77
+ const grpprl = [3, 106];
78
+ const operand = /* @__PURE__ */ new Uint8Array(4);
79
+ new DataView(operand.buffer).setInt32(0, dataStreamOffset, true);
80
+ grpprl.push(...operand);
81
+ return grpprl;
82
+ }
83
+ //#endregion
84
+ export { buildInlinePicture };
package/dist/pictures.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_base64 = require("./base64.cjs");
2
3
  const require_bytes = require("./bytes.cjs");
3
4
  //#region src/pictures.ts
4
5
  const PICF_SIZE = 68;
@@ -59,10 +60,11 @@ function readInlinePicture(dataStream, picLocation) {
59
60
  if (uidBytes === void 0) return void 0;
60
61
  const blipDataStart = cursor + RECORD_HEADER_SIZE + uidBytes + BLIP_TAG_SIZE;
61
62
  const blipDataLength = blipHeader.recLen - uidBytes - BLIP_TAG_SIZE;
63
+ const blipBytes = require_bytes.slice(dataStream, blipDataStart, blipDataLength, "OfficeArtBlip file data in the Data stream");
62
64
  return {
63
65
  kind: "image",
64
66
  format,
65
- base64: bytesToBase64(require_bytes.slice(dataStream, blipDataStart, blipDataLength, "OfficeArtBlip file data in the Data stream")),
67
+ base64: require_base64.bytesToBase64(blipBytes),
66
68
  widthPt: dxaGoal * mx / SCALE_DENOMINATOR / TWIPS_PER_POINT,
67
69
  heightPt: dyaGoal * my / SCALE_DENOMINATOR / TWIPS_PER_POINT
68
70
  };
@@ -74,20 +76,5 @@ function blipFormat(recType) {
74
76
  default: return;
75
77
  }
76
78
  }
77
- const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
78
- function bytesToBase64(bytes) {
79
- let out = "";
80
- const len = bytes.length;
81
- for (let index = 0; index < len; index += 3) {
82
- const b0 = bytes[index] ?? 0;
83
- const b1 = index + 1 < len ? bytes[index + 1] ?? 0 : 0;
84
- const b2 = index + 2 < len ? bytes[index + 2] ?? 0 : 0;
85
- out += BASE64_TABLE.charAt(b0 >> 2);
86
- out += BASE64_TABLE.charAt((b0 & 3) << 4 | b1 >> 4);
87
- out += index + 1 < len ? BASE64_TABLE.charAt((b1 & 15) << 2 | b2 >> 6) : "=";
88
- out += index + 2 < len ? BASE64_TABLE.charAt(b2 & 63) : "=";
89
- }
90
- return out;
91
- }
92
79
  //#endregion
93
80
  exports.readInlinePicture = readInlinePicture;
package/dist/pictures.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { bytesToBase64 } from "./base64.js";
1
2
  import { readInt16LE, readUint16LE, readUint32LE, readUint8, slice } from "./bytes.js";
2
3
  //#region src/pictures.ts
3
4
  const PICF_SIZE = 68;
@@ -58,10 +59,11 @@ function readInlinePicture(dataStream, picLocation) {
58
59
  if (uidBytes === void 0) return void 0;
59
60
  const blipDataStart = cursor + RECORD_HEADER_SIZE + uidBytes + BLIP_TAG_SIZE;
60
61
  const blipDataLength = blipHeader.recLen - uidBytes - BLIP_TAG_SIZE;
62
+ const blipBytes = slice(dataStream, blipDataStart, blipDataLength, "OfficeArtBlip file data in the Data stream");
61
63
  return {
62
64
  kind: "image",
63
65
  format,
64
- base64: bytesToBase64(slice(dataStream, blipDataStart, blipDataLength, "OfficeArtBlip file data in the Data stream")),
66
+ base64: bytesToBase64(blipBytes),
65
67
  widthPt: dxaGoal * mx / SCALE_DENOMINATOR / TWIPS_PER_POINT,
66
68
  heightPt: dyaGoal * my / SCALE_DENOMINATOR / TWIPS_PER_POINT
67
69
  };
@@ -73,20 +75,5 @@ function blipFormat(recType) {
73
75
  default: return;
74
76
  }
75
77
  }
76
- const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
77
- function bytesToBase64(bytes) {
78
- let out = "";
79
- const len = bytes.length;
80
- for (let index = 0; index < len; index += 3) {
81
- const b0 = bytes[index] ?? 0;
82
- const b1 = index + 1 < len ? bytes[index + 1] ?? 0 : 0;
83
- const b2 = index + 2 < len ? bytes[index + 2] ?? 0 : 0;
84
- out += BASE64_TABLE.charAt(b0 >> 2);
85
- out += BASE64_TABLE.charAt((b0 & 3) << 4 | b1 >> 4);
86
- out += index + 1 < len ? BASE64_TABLE.charAt((b1 & 15) << 2 | b2 >> 6) : "=";
87
- out += index + 2 < len ? BASE64_TABLE.charAt(b2 & 63) : "=";
88
- }
89
- return out;
90
- }
91
78
  //#endregion
92
79
  export { readInlinePicture };
@@ -45,16 +45,23 @@ function buildSepx(grpprl) {
45
45
  bytes.set(grpprl, 2);
46
46
  return bytes;
47
47
  }
48
- /** PlcfSed for exactly one section: two CPs (0 and ccpText) bracketing the single Sed ([MS-DOC] 2.9.269) this writer ever emits, whose fcSepx names where buildSepx's own bytes were placed in the WordDocument stream and whose fn/fnMpr/fcMpr fields carry the values [MS-DOC] states are ignored. */
49
- function buildPlcfSed(ccpText, fcSepx) {
50
- const bytes = /* @__PURE__ */ new Uint8Array(20);
48
+ /** PlcfSed for `startCps.length` sections, [MS-DOC] 2.9.269/2.8.26: `startCps` (each section's own PlcfSed.aCp[i], "the beginning of a range of text ... that constitutes a section") plus a trailing `ccpText` -- the "last CP does not begin a new section" terminator -- bracketing one 12-byte Sed per section, each naming where that section's own buildSepx bytes were placed in the WordDocument stream (`fcSepxList`, the same order as `startCps`) and whose fn/fnMpr/fcMpr fields carry the values [MS-DOC] states are ignored. A single-section document is simply the `startCps.length === 1` case. */
49
+ function buildPlcfSed(startCps, ccpText, fcSepxList) {
50
+ if (startCps.length !== fcSepxList.length) throw new require_errors.DocFormatError(`internal defect: buildPlcfSed was given ${String(startCps.length)} section start CPs but ${String(fcSepxList.length)} Sepx offsets -- these must be the same length`);
51
+ const keys = [...startCps, ccpText];
52
+ const keyBytes = keys.length * 4;
53
+ const bytes = new Uint8Array(keyBytes + fcSepxList.length * 12);
51
54
  const view = new DataView(bytes.buffer);
52
- view.setUint32(0, 0, true);
53
- view.setUint32(4, ccpText, true);
54
- view.setUint16(8, 0, true);
55
- view.setUint32(10, fcSepx, true);
56
- view.setUint16(14, 0, true);
57
- view.setUint32(16, 4294967295, true);
55
+ keys.forEach((cp, index) => {
56
+ view.setUint32(index * 4, cp, true);
57
+ });
58
+ fcSepxList.forEach((fcSepx, index) => {
59
+ const base = keyBytes + index * 12;
60
+ view.setUint16(base, 0, true);
61
+ view.setUint32(base + 2, fcSepx, true);
62
+ view.setUint16(base + 6, 0, true);
63
+ view.setUint32(base + 8, 4294967295, true);
64
+ });
58
65
  return bytes;
59
66
  }
60
67
  //#endregion
@@ -3,7 +3,7 @@ import { ContentSection } from "document-schema.js";
3
3
  declare function encodeSectionGrpprl(section: Pick<ContentSection, "pageSize" | "margins">): number[];
4
4
  /** Sepx, [MS-DOC] 2.9.279: a 2-byte cb (grpprl's own length) followed by the grpprl itself. */
5
5
  declare function buildSepx(grpprl: readonly number[]): Uint8Array<ArrayBuffer>;
6
- /** PlcfSed for exactly one section: two CPs (0 and ccpText) bracketing the single Sed ([MS-DOC] 2.9.269) this writer ever emits, whose fcSepx names where buildSepx's own bytes were placed in the WordDocument stream and whose fn/fnMpr/fcMpr fields carry the values [MS-DOC] states are ignored. */
7
- declare function buildPlcfSed(ccpText: number, fcSepx: number): Uint8Array<ArrayBuffer>;
6
+ /** PlcfSed for `startCps.length` sections, [MS-DOC] 2.9.269/2.8.26: `startCps` (each section's own PlcfSed.aCp[i], "the beginning of a range of text ... that constitutes a section") plus a trailing `ccpText` -- the "last CP does not begin a new section" terminator -- bracketing one 12-byte Sed per section, each naming where that section's own buildSepx bytes were placed in the WordDocument stream (`fcSepxList`, the same order as `startCps`) and whose fn/fnMpr/fcMpr fields carry the values [MS-DOC] states are ignored. A single-section document is simply the `startCps.length === 1` case. */
7
+ declare function buildPlcfSed(startCps: readonly number[], ccpText: number, fcSepxList: readonly number[]): Uint8Array<ArrayBuffer>;
8
8
  //#endregion
9
9
  export { buildPlcfSed, buildSepx, encodeSectionGrpprl };
@@ -3,7 +3,7 @@ import { ContentSection } from "document-schema.js";
3
3
  declare function encodeSectionGrpprl(section: Pick<ContentSection, "pageSize" | "margins">): number[];
4
4
  /** Sepx, [MS-DOC] 2.9.279: a 2-byte cb (grpprl's own length) followed by the grpprl itself. */
5
5
  declare function buildSepx(grpprl: readonly number[]): Uint8Array<ArrayBuffer>;
6
- /** PlcfSed for exactly one section: two CPs (0 and ccpText) bracketing the single Sed ([MS-DOC] 2.9.269) this writer ever emits, whose fcSepx names where buildSepx's own bytes were placed in the WordDocument stream and whose fn/fnMpr/fcMpr fields carry the values [MS-DOC] states are ignored. */
7
- declare function buildPlcfSed(ccpText: number, fcSepx: number): Uint8Array<ArrayBuffer>;
6
+ /** PlcfSed for `startCps.length` sections, [MS-DOC] 2.9.269/2.8.26: `startCps` (each section's own PlcfSed.aCp[i], "the beginning of a range of text ... that constitutes a section") plus a trailing `ccpText` -- the "last CP does not begin a new section" terminator -- bracketing one 12-byte Sed per section, each naming where that section's own buildSepx bytes were placed in the WordDocument stream (`fcSepxList`, the same order as `startCps`) and whose fn/fnMpr/fcMpr fields carry the values [MS-DOC] states are ignored. A single-section document is simply the `startCps.length === 1` case. */
7
+ declare function buildPlcfSed(startCps: readonly number[], ccpText: number, fcSepxList: readonly number[]): Uint8Array<ArrayBuffer>;
8
8
  //#endregion
9
9
  export { buildPlcfSed, buildSepx, encodeSectionGrpprl };
@@ -44,16 +44,23 @@ function buildSepx(grpprl) {
44
44
  bytes.set(grpprl, 2);
45
45
  return bytes;
46
46
  }
47
- /** PlcfSed for exactly one section: two CPs (0 and ccpText) bracketing the single Sed ([MS-DOC] 2.9.269) this writer ever emits, whose fcSepx names where buildSepx's own bytes were placed in the WordDocument stream and whose fn/fnMpr/fcMpr fields carry the values [MS-DOC] states are ignored. */
48
- function buildPlcfSed(ccpText, fcSepx) {
49
- const bytes = /* @__PURE__ */ new Uint8Array(20);
47
+ /** PlcfSed for `startCps.length` sections, [MS-DOC] 2.9.269/2.8.26: `startCps` (each section's own PlcfSed.aCp[i], "the beginning of a range of text ... that constitutes a section") plus a trailing `ccpText` -- the "last CP does not begin a new section" terminator -- bracketing one 12-byte Sed per section, each naming where that section's own buildSepx bytes were placed in the WordDocument stream (`fcSepxList`, the same order as `startCps`) and whose fn/fnMpr/fcMpr fields carry the values [MS-DOC] states are ignored. A single-section document is simply the `startCps.length === 1` case. */
48
+ function buildPlcfSed(startCps, ccpText, fcSepxList) {
49
+ if (startCps.length !== fcSepxList.length) throw new DocFormatError(`internal defect: buildPlcfSed was given ${String(startCps.length)} section start CPs but ${String(fcSepxList.length)} Sepx offsets -- these must be the same length`);
50
+ const keys = [...startCps, ccpText];
51
+ const keyBytes = keys.length * 4;
52
+ const bytes = new Uint8Array(keyBytes + fcSepxList.length * 12);
50
53
  const view = new DataView(bytes.buffer);
51
- view.setUint32(0, 0, true);
52
- view.setUint32(4, ccpText, true);
53
- view.setUint16(8, 0, true);
54
- view.setUint32(10, fcSepx, true);
55
- view.setUint16(14, 0, true);
56
- view.setUint32(16, 4294967295, true);
54
+ keys.forEach((cp, index) => {
55
+ view.setUint32(index * 4, cp, true);
56
+ });
57
+ fcSepxList.forEach((fcSepx, index) => {
58
+ const base = keyBytes + index * 12;
59
+ view.setUint16(base, 0, true);
60
+ view.setUint32(base + 2, fcSepx, true);
61
+ view.setUint16(base + 6, 0, true);
62
+ view.setUint32(base + 8, 4294967295, true);
63
+ });
57
64
  return bytes;
58
65
  }
59
66
  //#endregion
@@ -1,7 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_errors = require("../errors.cjs");
3
+ require("../data-stream.cjs");
3
4
  require("../text/special.cjs");
4
5
  const require_prop_fkp_write = require("../prop/fkp-write.cjs");
6
+ const require_pictures_write = require("../pictures-write.cjs");
5
7
  const require_table_tap_write = require("./tap-write.cjs");
6
8
  //#region src/table/write.ts
7
9
  /** sprmPFInTable (0x2416): a Bool8, "MUST be 1 any time the table depth is greater than zero". */
@@ -9,6 +11,12 @@ const SPRM_P_F_IN_TABLE = 9238;
9
11
  /** sprmPFTtp (0x2417): a Bool8 marking a cell mark as the row's own Table Terminating Paragraph mark. */
10
12
  const SPRM_P_F_TTP = 9239;
11
13
  const TWIPS_PER_POINT = 20;
14
+ function plainRuns(runs) {
15
+ return runs.map((run) => ({
16
+ run,
17
+ extraGrpprl: []
18
+ }));
19
+ }
12
20
  function pushSprm(bytes, opcode, operand) {
13
21
  bytes.push(opcode & 255, opcode >> 8 & 255, ...operand);
14
22
  }
@@ -33,7 +41,7 @@ function cellParagraphs(blocks) {
33
41
  return blocks.map((block, index) => {
34
42
  if (block.kind !== "paragraph") throw new require_errors.DocUnsupportedError(`doc-codec's writer does not support a '${block.kind}' block inside a table cell (only paragraphs are; nested tables are a separately-tracked gap -- see the README's scope note)`);
35
43
  return {
36
- runs: block.runs,
44
+ runs: plainRuns(block.runs),
37
45
  properties: block,
38
46
  extraGrpprl: inTableGrpprl(),
39
47
  terminator: index === blocks.length - 1 ? 7 : 13
@@ -189,12 +197,25 @@ function flattenTable(table, blockIndex, onWarning) {
189
197
  });
190
198
  return output;
191
199
  }
192
- function flattenSectionBlocks(blocks, onWarning) {
200
+ function imageParagraph(image, dataStream) {
201
+ const { data, buildGrpprl } = require_pictures_write.buildInlinePicture(image);
202
+ const offset = dataStream.append(data);
203
+ return {
204
+ runs: [{
205
+ run: { text: String.fromCharCode(1) },
206
+ extraGrpprl: buildGrpprl(offset)
207
+ }],
208
+ properties: {},
209
+ extraGrpprl: [],
210
+ terminator: 13
211
+ };
212
+ }
213
+ function flattenSectionBlocks(blocks, dataStream, onWarning) {
193
214
  const output = [];
194
215
  blocks.forEach((block, blockIndex) => {
195
216
  if (block.kind === "paragraph") {
196
217
  output.push({
197
- runs: block.runs,
218
+ runs: plainRuns(block.runs),
198
219
  properties: block,
199
220
  extraGrpprl: [],
200
221
  terminator: 13
@@ -205,6 +226,10 @@ function flattenSectionBlocks(blocks, onWarning) {
205
226
  output.push(...flattenTable(block, blockIndex, onWarning));
206
227
  return;
207
228
  }
229
+ if (block.kind === "image") {
230
+ output.push(imageParagraph(block, dataStream));
231
+ return;
232
+ }
208
233
  throw new require_errors.DocUnsupportedError(`doc-codec's writer does not yet support '${block.kind}' blocks (see README's scope note)`);
209
234
  });
210
235
  return output;
@@ -1,2 +1,2 @@
1
- import { n as WriteWarning, r as flattenSectionBlocks, t as WriteParagraph } from "../write-SN_aIwyF.cjs";
2
- export { WriteParagraph, WriteWarning, flattenSectionBlocks };
1
+ import { i as flattenSectionBlocks, n as WriteRun, r as WriteWarning, t as WriteParagraph } from "../write-CMB3qF4i.cjs";
2
+ export { WriteParagraph, WriteRun, WriteWarning, flattenSectionBlocks };
@@ -1,2 +1,2 @@
1
- import { n as WriteWarning, r as flattenSectionBlocks, t as WriteParagraph } from "../write-SN_aIwyF.js";
2
- export { WriteParagraph, WriteWarning, flattenSectionBlocks };
1
+ import { i as flattenSectionBlocks, n as WriteRun, r as WriteWarning, t as WriteParagraph } from "../write-t4rFgnA-.js";
2
+ export { WriteParagraph, WriteRun, WriteWarning, flattenSectionBlocks };
@@ -1,6 +1,8 @@
1
1
  import { DocFormatError, DocUnsupportedError } from "../errors.js";
2
+ import "../data-stream.js";
2
3
  import "../text/special.js";
3
4
  import { fitsAloneOnPapxPage } from "../prop/fkp-write.js";
5
+ import { buildInlinePicture } from "../pictures-write.js";
4
6
  import { encodeTableRowGrpprl } from "./tap-write.js";
5
7
  //#region src/table/write.ts
6
8
  /** sprmPFInTable (0x2416): a Bool8, "MUST be 1 any time the table depth is greater than zero". */
@@ -8,6 +10,12 @@ const SPRM_P_F_IN_TABLE = 9238;
8
10
  /** sprmPFTtp (0x2417): a Bool8 marking a cell mark as the row's own Table Terminating Paragraph mark. */
9
11
  const SPRM_P_F_TTP = 9239;
10
12
  const TWIPS_PER_POINT = 20;
13
+ function plainRuns(runs) {
14
+ return runs.map((run) => ({
15
+ run,
16
+ extraGrpprl: []
17
+ }));
18
+ }
11
19
  function pushSprm(bytes, opcode, operand) {
12
20
  bytes.push(opcode & 255, opcode >> 8 & 255, ...operand);
13
21
  }
@@ -32,7 +40,7 @@ function cellParagraphs(blocks) {
32
40
  return blocks.map((block, index) => {
33
41
  if (block.kind !== "paragraph") throw new DocUnsupportedError(`doc-codec's writer does not support a '${block.kind}' block inside a table cell (only paragraphs are; nested tables are a separately-tracked gap -- see the README's scope note)`);
34
42
  return {
35
- runs: block.runs,
43
+ runs: plainRuns(block.runs),
36
44
  properties: block,
37
45
  extraGrpprl: inTableGrpprl(),
38
46
  terminator: index === blocks.length - 1 ? 7 : 13
@@ -188,12 +196,25 @@ function flattenTable(table, blockIndex, onWarning) {
188
196
  });
189
197
  return output;
190
198
  }
191
- function flattenSectionBlocks(blocks, onWarning) {
199
+ function imageParagraph(image, dataStream) {
200
+ const { data, buildGrpprl } = buildInlinePicture(image);
201
+ const offset = dataStream.append(data);
202
+ return {
203
+ runs: [{
204
+ run: { text: String.fromCharCode(1) },
205
+ extraGrpprl: buildGrpprl(offset)
206
+ }],
207
+ properties: {},
208
+ extraGrpprl: [],
209
+ terminator: 13
210
+ };
211
+ }
212
+ function flattenSectionBlocks(blocks, dataStream, onWarning) {
192
213
  const output = [];
193
214
  blocks.forEach((block, blockIndex) => {
194
215
  if (block.kind === "paragraph") {
195
216
  output.push({
196
- runs: block.runs,
217
+ runs: plainRuns(block.runs),
197
218
  properties: block,
198
219
  extraGrpprl: [],
199
220
  terminator: 13
@@ -204,6 +225,10 @@ function flattenSectionBlocks(blocks, onWarning) {
204
225
  output.push(...flattenTable(block, blockIndex, onWarning));
205
226
  return;
206
227
  }
228
+ if (block.kind === "image") {
229
+ output.push(imageParagraph(block, dataStream));
230
+ return;
231
+ }
207
232
  throw new DocUnsupportedError(`doc-codec's writer does not yet support '${block.kind}' blocks (see README's scope note)`);
208
233
  });
209
234
  return output;
@@ -1,15 +1,21 @@
1
+ import { t as DataStreamBuilder } from "./data-stream-BLCj4-V1.cjs";
1
2
  import { ContentBlock, ContentParagraph, ContentRun } from "document-schema.js";
2
3
  //#region src/table/write.d.ts
3
4
  /** Reports a non-fatal write-time degradation -- this package's own analogue of byte-codec's/pdf-codec's `onWarning`, adopted here rather than a new shape of its own so a caller already handling one already handles the other. */
4
5
  type WriteWarning = (message: string) => void;
6
+ /** One run to write, alongside grpprl bytes appended after encodeCharacterGrpprl's own output for it -- the run-level analogue of WriteParagraph.extraGrpprl below. Every ordinary run carries none (its whole grpprl comes from its own ContentRun fields); imageParagraph's own picture-anchor run is the one exception, since sprmCPicLocation is not a ContentRun field encodeCharacterGrpprl could ever derive on its own. */
7
+ interface WriteRun {
8
+ readonly run: ContentRun;
9
+ readonly extraGrpprl: readonly number[];
10
+ }
5
11
  interface WriteParagraph {
6
- readonly runs: readonly ContentRun[];
12
+ readonly runs: readonly WriteRun[];
7
13
  readonly properties: Pick<ContentParagraph, "alignment" | "indentLeftPt" | "indentRightPt" | "indentFirstLinePt" | "spacingBeforePt" | "spacingAfterPt" | "lineSpacing" | "pageBreakBefore" | "list" | "styleId" | "headingLevel">;
8
14
  /** Extra grpprl bytes appended after encodeParagraphGrpprl's own output -- sprmPFInTable on every table paragraph, plus sprmPFTtp and the row's own TAP on a row's trailing mark. */
9
15
  readonly extraGrpprl: readonly number[];
10
16
  /** The character terminating this paragraph in the text stream: PARAGRAPH_MARK normally, CELL_MARK for a table cell or row mark. */
11
17
  readonly terminator: number;
12
18
  }
13
- declare function flattenSectionBlocks(blocks: readonly ContentBlock[], onWarning?: WriteWarning): WriteParagraph[];
19
+ declare function flattenSectionBlocks(blocks: readonly ContentBlock[], dataStream: DataStreamBuilder, onWarning?: WriteWarning): WriteParagraph[];
14
20
  //#endregion
15
- export { WriteWarning as n, flattenSectionBlocks as r, WriteParagraph as t };
21
+ export { flattenSectionBlocks as i, WriteRun as n, WriteWarning as r, WriteParagraph as t };
@@ -1,15 +1,21 @@
1
+ import { t as DataStreamBuilder } from "./data-stream-BLCj4-V1.js";
1
2
  import { ContentBlock, ContentParagraph, ContentRun } from "document-schema.js";
2
3
  //#region src/table/write.d.ts
3
4
  /** Reports a non-fatal write-time degradation -- this package's own analogue of byte-codec's/pdf-codec's `onWarning`, adopted here rather than a new shape of its own so a caller already handling one already handles the other. */
4
5
  type WriteWarning = (message: string) => void;
6
+ /** One run to write, alongside grpprl bytes appended after encodeCharacterGrpprl's own output for it -- the run-level analogue of WriteParagraph.extraGrpprl below. Every ordinary run carries none (its whole grpprl comes from its own ContentRun fields); imageParagraph's own picture-anchor run is the one exception, since sprmCPicLocation is not a ContentRun field encodeCharacterGrpprl could ever derive on its own. */
7
+ interface WriteRun {
8
+ readonly run: ContentRun;
9
+ readonly extraGrpprl: readonly number[];
10
+ }
5
11
  interface WriteParagraph {
6
- readonly runs: readonly ContentRun[];
12
+ readonly runs: readonly WriteRun[];
7
13
  readonly properties: Pick<ContentParagraph, "alignment" | "indentLeftPt" | "indentRightPt" | "indentFirstLinePt" | "spacingBeforePt" | "spacingAfterPt" | "lineSpacing" | "pageBreakBefore" | "list" | "styleId" | "headingLevel">;
8
14
  /** Extra grpprl bytes appended after encodeParagraphGrpprl's own output -- sprmPFInTable on every table paragraph, plus sprmPFTtp and the row's own TAP on a row's trailing mark. */
9
15
  readonly extraGrpprl: readonly number[];
10
16
  /** The character terminating this paragraph in the text stream: PARAGRAPH_MARK normally, CELL_MARK for a table cell or row mark. */
11
17
  readonly terminator: number;
12
18
  }
13
- declare function flattenSectionBlocks(blocks: readonly ContentBlock[], onWarning?: WriteWarning): WriteParagraph[];
19
+ declare function flattenSectionBlocks(blocks: readonly ContentBlock[], dataStream: DataStreamBuilder, onWarning?: WriteWarning): WriteParagraph[];
14
20
  //#endregion
15
- export { WriteWarning as n, flattenSectionBlocks as r, WriteParagraph as t };
21
+ export { flattenSectionBlocks as i, WriteRun as n, WriteWarning as r, WriteParagraph as t };