reamkit 1.17.0 → 1.19.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.
Files changed (34) hide show
  1. package/dist/esm/core/document-model/types.d.ts +59 -1
  2. package/dist/esm/core/drawingml/colors.js +13 -6
  3. package/dist/esm/core/images.d.ts +2 -2
  4. package/dist/esm/core/images.js +38 -0
  5. package/dist/esm/core/lzw.d.ts +17 -0
  6. package/dist/esm/core/lzw.js +83 -0
  7. package/dist/esm/core/metafile/emf.d.ts +16 -0
  8. package/dist/esm/core/metafile/emf.js +635 -0
  9. package/dist/esm/core/metafile/picture.d.ts +106 -0
  10. package/dist/esm/core/metafile/picture.js +55 -0
  11. package/dist/esm/core/metafile/wmf.d.ts +9 -0
  12. package/dist/esm/core/metafile/wmf.js +371 -0
  13. package/dist/esm/core/po-helpers.d.ts +9 -0
  14. package/dist/esm/core/po-helpers.js +9 -1
  15. package/dist/esm/core/tiff.d.ts +20 -0
  16. package/dist/esm/core/tiff.js +339 -0
  17. package/dist/esm/core/vector.d.ts +5 -0
  18. package/dist/esm/excel/print-model.js +3 -1
  19. package/dist/esm/html/html-writer.js +3 -2
  20. package/dist/esm/layout/page-doc.d.ts +46 -3
  21. package/dist/esm/layout/styled-layout.js +649 -108
  22. package/dist/esm/pdf/styled-page-emitter.js +78 -3
  23. package/dist/esm/pdf/vector-graphics.d.ts +1 -1
  24. package/dist/esm/pdf/vector-graphics.js +2 -1
  25. package/dist/esm/pdf-reader/image-decode.js +7 -75
  26. package/dist/esm/svg/svg-writer.js +1 -1
  27. package/dist/esm/word/document-parser.js +3 -0
  28. package/dist/esm/word/drawing-parser.d.ts +5 -0
  29. package/dist/esm/word/drawing-parser.js +100 -14
  30. package/dist/esm/word/paragraph-properties.js +2 -2
  31. package/dist/esm/word/run-properties.js +2 -2
  32. package/dist/esm/word/xml-helpers.d.ts +12 -0
  33. package/dist/esm/word/xml-helpers.js +23 -3
  34. package/package.json +1 -1
@@ -248,6 +248,15 @@ export interface InlineImage {
248
248
  * six stood flat on the page where both references lift them off it.
249
249
  */
250
250
  readonly shadow?: ShapeShadow;
251
+ /**
252
+ * §14.1.2.10 `@gain`/`@blacklevel` — the contrast and brightness the picture
253
+ * is drawn through, about mid grey: `out = (in - 0.5) * gain + 0.5 + black`.
254
+ * Word washes a watermark out this way.
255
+ */
256
+ readonly wash?: {
257
+ readonly gain: number;
258
+ readonly black: number;
259
+ };
251
260
  readonly width: Pt;
252
261
  readonly height: Pt;
253
262
  /** §20.1.8.55 `a:srcRect` — the part of the source the frame shows. */
@@ -897,6 +906,15 @@ export interface ImageBlock {
897
906
  * six stood flat on the page where both references lift them off it.
898
907
  */
899
908
  readonly shadow?: ShapeShadow;
909
+ /**
910
+ * §14.1.2.10 `@gain`/`@blacklevel` — the contrast and brightness the picture
911
+ * is drawn through, about mid grey: `out = (in - 0.5) * gain + 0.5 + black`.
912
+ * Word washes a watermark out this way.
913
+ */
914
+ readonly wash?: {
915
+ readonly gain: number;
916
+ readonly black: number;
917
+ };
900
918
  readonly width: Pt;
901
919
  readonly height: Pt;
902
920
  /** §20.1.8.55 `a:srcRect` — the part of the source the frame shows. */
@@ -974,6 +992,12 @@ export interface ShapeFill {
974
992
  readonly kind: ShapeFillKind;
975
993
  readonly colorHex?: string;
976
994
  readonly gradient?: ShapeGradient;
995
+ /**
996
+ * §14.1.2.5 `@type="tile"` — the picture REPEATS at its own size rather than
997
+ * stretching over the box. A parchment behind a page, a texture behind a
998
+ * text box: stretched, such a fill is a blur where it should be a pattern.
999
+ */
1000
+ readonly tiled?: boolean;
977
1001
  /**
978
1002
  * §20.1.8.14 `a:blipFill` — the picture painted across the shape's box. A
979
1003
  * DrawingML picture IS a shape with one of these, which is how a `pic:pic`
@@ -985,6 +1009,12 @@ export interface ShapeFill {
985
1009
  * of the picture the box shows, as the fractions cut from each side.
986
1010
  */
987
1011
  readonly imageCrop?: ImageCrop;
1012
+ /**
1013
+ * §20.1.2.3.1 `a:alpha` / §14.1.2.5 `@opacity` — how opaque the fill is,
1014
+ * `0..1`. Absent is opaque. The colour above is the fill's own, NOT composited
1015
+ * over the paper: what is behind the shape shows through it.
1016
+ */
1017
+ readonly alpha?: number;
988
1018
  }
989
1019
  /** §20.1.10.49 ST_PresetLineDashVal — a shape outline's preset dash pattern. */
990
1020
  export type ShapeDash = 'solid' | 'dot' | 'dash' | 'dashDot' | 'lgDash' | 'lgDashDot' | 'sysDash' | 'sysDot';
@@ -1063,6 +1093,16 @@ export interface ShapeTextBody {
1063
1093
  * WordArt is set at whatever size fills the box it was drawn in.
1064
1094
  */
1065
1095
  readonly fitToBox?: boolean;
1096
+ /**
1097
+ * `wps:txbx @id` / `wps:linkedTxbx @id @seq` — the chain of boxes this one
1098
+ * belongs to. Text that overruns a box continues in the next of its chain;
1099
+ * `seq` 0 is the box that holds the words, and the rest carry none of their
1100
+ * own.
1101
+ */
1102
+ readonly chain?: {
1103
+ readonly id: string;
1104
+ readonly seq: number;
1105
+ };
1066
1106
  }
1067
1107
  /**
1068
1108
  * A block-level DrawingML shape (§20.1): its size, {@link ShapeGeometry}, fill,
@@ -1417,7 +1457,12 @@ export interface SectionProperties {
1417
1457
  * modes (square/tight/through) and `topAndBottom` stay in flow as blocks.
1418
1458
  */
1419
1459
  export interface FloatAnchor {
1420
- readonly wrap: 'none' | 'square' | 'tight' | 'through' | 'topAndBottom';
1460
+ /**
1461
+ * §20.4.2.3 / §17.18.104 — how body text runs past the drawing. `notBeside`
1462
+ * is the FRAME's own mode: the drawing keeps the place its anchor names, but
1463
+ * no text may stand beside it, so its band spans the whole column.
1464
+ */
1465
+ readonly wrap: 'none' | 'square' | 'tight' | 'through' | 'topAndBottom' | 'notBeside';
1421
1466
  readonly behind?: boolean;
1422
1467
  /**
1423
1468
  * §20.4.2.3 `wp:anchor @relativeHeight` — the z-order among the floats on the
@@ -1451,6 +1496,19 @@ export interface FloatAnchor {
1451
1496
  */
1452
1497
  readonly align?: 'top' | 'center' | 'bottom';
1453
1498
  };
1499
+ /**
1500
+ * §20.4.2.3 `@wrapText` on the wrap element — which side(s) of the drawing
1501
+ * text may stand on. `bothSides` is the default and the only value that
1502
+ * fills BOTH gaps; `left`/`right` name one, `largest` the wider.
1503
+ */
1504
+ readonly wrapSide?: 'bothSides' | 'left' | 'right' | 'largest';
1505
+ /**
1506
+ * §20.4.2.3 `wp:anchor @layoutInCell` (VML `o:allowincell`) — present and
1507
+ * false when a drawing anchored inside a TABLE CELL is placed against the
1508
+ * page rather than against the cell. Absent means the default: the cell is
1509
+ * the frame every `relativeFrom` is measured in.
1510
+ */
1511
+ readonly inCell?: boolean;
1454
1512
  /**
1455
1513
  * §20.4.2.3 `wp:anchor @distT/@distB/@distL/@distR` — how far the wrapped
1456
1514
  * text stands off each edge of the drawing. Absent sides are 0.
@@ -69,6 +69,13 @@ function hslToRgb(h, s, l) {
69
69
  b + m
70
70
  ];
71
71
  }
72
+ function toLinear(c) {
73
+ return c <= .04045 ? c / 12.92 : Math.pow((c + .055) / 1.055, 2.4);
74
+ }
75
+ function toSrgb(c) {
76
+ const v = clamp01(c);
77
+ return v <= .0031308 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - .055;
78
+ }
72
79
  /**
73
80
  * Apply DrawingML colour transforms ({@link ColorMod}s) to a 6-hex value,
74
81
  * returning a 6-hex value. `shade`/`tint` scale in RGB; `lumMod`/`lumOff` adjust
@@ -85,13 +92,13 @@ function applyColorMods(hex, mods) {
85
92
  let g = (n >> 8 & 255) / 255;
86
93
  let b = (n & 255) / 255;
87
94
  for (const m of mods) if (m.kind === "shade") {
88
- r *= m.val;
89
- g *= m.val;
90
- b *= m.val;
95
+ r = toSrgb(toLinear(r) * m.val);
96
+ g = toSrgb(toLinear(g) * m.val);
97
+ b = toSrgb(toLinear(b) * m.val);
91
98
  } else if (m.kind === "tint") {
92
- r = r * m.val + (1 - m.val);
93
- g = g * m.val + (1 - m.val);
94
- b = b * m.val + (1 - m.val);
99
+ r = toSrgb(toLinear(r) * m.val + (1 - m.val));
100
+ g = toSrgb(toLinear(g) * m.val + (1 - m.val));
101
+ b = toSrgb(toLinear(b) * m.val + (1 - m.val));
95
102
  } else if (m.kind === "lumMod" || m.kind === "lumOff") {
96
103
  const [h, s, l] = rgbToHsl(r, g, b);
97
104
  const l2 = m.kind === "lumMod" ? l * m.val : clamp01(l + m.val);
@@ -1,5 +1,5 @@
1
1
  /** The raster formats this module recognizes and can prepare for embedding. */
2
- export type ImageFormat = 'jpeg' | 'png' | 'jpeg2000' | 'gif';
2
+ export type ImageFormat = 'jpeg' | 'png' | 'jpeg2000' | 'gif' | 'tiff';
3
3
  /**
4
4
  * Sniff the raster format from a file's leading magic bytes (JPEG SOI, the PNG
5
5
  * signature, or a JP2 box / raw JPEG 2000 codestream). Returns the
@@ -24,7 +24,7 @@ export interface EmbedImageOptions {
24
24
  */
25
25
  export interface PreparedImage {
26
26
  readonly format: ImageFormat;
27
- readonly mimeType: 'image/jp2' | 'image/jpeg' | 'image/png' | 'image/gif';
27
+ readonly mimeType: 'image/jp2' | 'image/jpeg' | 'image/png' | 'image/gif' | 'image/tiff';
28
28
  readonly widthPx: number;
29
29
  readonly heightPx: number;
30
30
  /**
@@ -1,3 +1,4 @@
1
+ import { decodeTiff, isTiff } from "./tiff.js";
1
2
  import { unzlibSync, zlibSync } from "fflate";
2
3
  //#region src/core/images.ts
3
4
  /**
@@ -11,6 +12,7 @@ function detectImageFormat(bytes) {
11
12
  if (bytes.length >= 12 && bytes[0] === 0 && bytes[1] === 0 && bytes[2] === 0 && bytes[3] === 12 && bytes[4] === 106 && bytes[5] === 80 && bytes[6] === 32 && bytes[7] === 32 && bytes[8] === 13 && bytes[9] === 10 && bytes[10] === 135 && bytes[11] === 10) return "jpeg2000";
12
13
  if (bytes.length >= 4 && bytes[0] === 255 && bytes[1] === 79 && bytes[2] === 255 && bytes[3] === 81) return "jpeg2000";
13
14
  if (bytes.length >= 6 && bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 56 && (bytes[4] === 55 || bytes[4] === 57) && bytes[5] === 97) return "gif";
15
+ if (isTiff(bytes)) return "tiff";
14
16
  return null;
15
17
  }
16
18
  /**
@@ -26,6 +28,7 @@ function prepareImage(bytes, options = {}) {
26
28
  if (format === "png") return preparePng(bytes, options);
27
29
  if (format === "jpeg2000") return prepareJpeg2000(bytes);
28
30
  if (format === "gif") return prepareGif(bytes, options);
31
+ if (format === "tiff") return prepareTiff(bytes, options);
29
32
  throw new Error("Unsupported image format");
30
33
  }
31
34
  function prepareJpeg2000(bytes) {
@@ -170,6 +173,41 @@ function prepareGif(bytes, options = {}) {
170
173
  ...alpha && !options.flattenAlpha ? { smaskData: zlibSync(alpha) } : {}
171
174
  };
172
175
  }
176
+ function prepareTiff(bytes, options = {}) {
177
+ const img = decodeTiff(bytes);
178
+ const channels = img.channels === 4 ? 3 : img.channels;
179
+ const data = img.channels === 4 ? cmykToRgb(img.data) : img.data.slice();
180
+ const alpha = img.alpha;
181
+ if (alpha && options.flattenAlpha) for (let i = 0; i < alpha.length; i++) {
182
+ if (alpha[i] === 255) continue;
183
+ const a = alpha[i] / 255;
184
+ for (let c = 0; c < channels; c++) {
185
+ const at = i * channels + c;
186
+ data[at] = Math.round(data[at] * a + 255 * (1 - a));
187
+ }
188
+ }
189
+ return {
190
+ format: "tiff",
191
+ mimeType: "image/tiff",
192
+ widthPx: img.width,
193
+ heightPx: img.height,
194
+ colorSpace: channels === 3 ? "DeviceRGB" : "DeviceGray",
195
+ bitsPerComponent: 8,
196
+ filter: "FlateDecode",
197
+ data: zlibSync(data),
198
+ ...alpha && !options.flattenAlpha ? { smaskData: zlibSync(alpha.slice()) } : {}
199
+ };
200
+ }
201
+ function cmykToRgb(cmyk) {
202
+ const out = new Uint8Array(cmyk.length / 4 * 3);
203
+ for (let i = 0; i * 4 + 3 < cmyk.length; i++) {
204
+ const k = 255 - cmyk[i * 4 + 3];
205
+ out[i * 3] = (255 - cmyk[i * 4]) * k / 255;
206
+ out[i * 3 + 1] = (255 - cmyk[i * 4 + 1]) * k / 255;
207
+ out[i * 3 + 2] = (255 - cmyk[i * 4 + 2]) * k / 255;
208
+ }
209
+ return out;
210
+ }
173
211
  function decodeGif(bytes) {
174
212
  const u16 = (i) => (bytes[i] ?? 0) | (bytes[i + 1] ?? 0) << 8;
175
213
  const screenW = u16(6);
@@ -0,0 +1,17 @@
1
+ /** How to run {@link lzwDecodeMsb}: the early-change flag and an output cap. */
2
+ export interface LzwOptions {
3
+ /**
4
+ * Widen the code one step BEFORE the table fills (PDF `/EarlyChange`,
5
+ * TIFF's own convention). 1 by default, matching both specifications' norm.
6
+ */
7
+ readonly earlyChange?: number;
8
+ /** Stop after this many output bytes — the guard against a decompression bomb. */
9
+ readonly limit: number;
10
+ }
11
+ /**
12
+ * Decode an MSB-first LZW stream (TIFF 6.0 §13 / ISO 32000-1 §7.4.4.2). The
13
+ * KwKwK case (a code not yet in the table) repeats the previous string plus its
14
+ * own first byte. Truncated input ends the stream where it stops rather than
15
+ * throwing: a partly-decoded strip still prints.
16
+ */
17
+ export declare function lzwDecodeMsb(data: Uint8Array, options: LzwOptions): Uint8Array;
@@ -0,0 +1,83 @@
1
+ //#region src/core/lzw.ts
2
+ /**
3
+ * Decode an MSB-first LZW stream (TIFF 6.0 §13 / ISO 32000-1 §7.4.4.2). The
4
+ * KwKwK case (a code not yet in the table) repeats the previous string plus its
5
+ * own first byte. Truncated input ends the stream where it stops rather than
6
+ * throwing: a partly-decoded strip still prints.
7
+ */
8
+ function lzwDecodeMsb(data, options) {
9
+ const earlyChange = options.earlyChange ?? 1;
10
+ let out = new Uint8Array(4096);
11
+ let outLen = 0;
12
+ const emit = (e) => {
13
+ if (outLen + e.length > options.limit) return false;
14
+ if (outLen + e.length > out.length) {
15
+ let cap = out.length * 2;
16
+ while (cap < outLen + e.length) cap *= 2;
17
+ const grown = new Uint8Array(cap);
18
+ grown.set(out.subarray(0, outLen));
19
+ out = grown;
20
+ }
21
+ out.set(e, outLen);
22
+ outLen += e.length;
23
+ return true;
24
+ };
25
+ let bitBuffer = 0;
26
+ let bitCount = 0;
27
+ let pos = 0;
28
+ let codeLength = 9;
29
+ const readCode = () => {
30
+ while (bitCount < codeLength) {
31
+ if (pos >= data.length) return -1;
32
+ bitBuffer = (bitBuffer << 8 | data[pos++]) >>> 0;
33
+ bitCount += 8;
34
+ }
35
+ bitCount -= codeLength;
36
+ return bitBuffer >>> bitCount & (1 << codeLength) - 1;
37
+ };
38
+ const dict = new Array(4096);
39
+ let nextCode = 258;
40
+ let prev = -1;
41
+ const reset = () => {
42
+ for (let i = 0; i < 256; i++) dict[i] = Uint8Array.of(i);
43
+ nextCode = 258;
44
+ codeLength = 9;
45
+ prev = -1;
46
+ };
47
+ reset();
48
+ for (;;) {
49
+ const code = readCode();
50
+ if (code < 0 || code === 257) break;
51
+ if (code === 256) {
52
+ reset();
53
+ continue;
54
+ }
55
+ if (prev < 0) {
56
+ const first = dict[code];
57
+ if (!first || !emit(first)) break;
58
+ prev = code;
59
+ continue;
60
+ }
61
+ const prevEntry = dict[prev];
62
+ let entry = code < nextCode ? dict[code] : void 0;
63
+ if (!entry) {
64
+ entry = new Uint8Array(prevEntry.length + 1);
65
+ entry.set(prevEntry);
66
+ entry[prevEntry.length] = prevEntry[0];
67
+ }
68
+ if (!emit(entry)) break;
69
+ if (nextCode < 4096) {
70
+ const added = new Uint8Array(prevEntry.length + 1);
71
+ added.set(prevEntry);
72
+ added[prevEntry.length] = entry[0];
73
+ dict[nextCode++] = added;
74
+ if (nextCode + earlyChange === 512) codeLength = 10;
75
+ else if (nextCode + earlyChange === 1024) codeLength = 11;
76
+ else if (nextCode + earlyChange === 2048) codeLength = 12;
77
+ }
78
+ prev = code;
79
+ }
80
+ return out.subarray(0, outLen);
81
+ }
82
+ //#endregion
83
+ export { lzwDecodeMsb };
@@ -0,0 +1,16 @@
1
+ import { PathSegment } from '../vector.js';
2
+ import { MetaPicture } from './picture.js';
3
+ /** Whether the bytes open with an EMF header (MS-EMF §2.3.4.2: type 1 + " EMF"). */
4
+ export declare function isEmf(bytes: Uint8Array): boolean;
5
+ /**
6
+ * Read an EMF into its primitives.
7
+ *
8
+ * @throws Error when the bytes are not an EMF or its header is malformed.
9
+ */
10
+ export declare function readEmf(bytes: Uint8Array): MetaPicture;
11
+ type Mapper = (x: number, y: number) => {
12
+ x: number;
13
+ y: number;
14
+ };
15
+ export declare function ellipseSegments(map: Mapper, l: number, t: number, r: number, b: number): Array<PathSegment>;
16
+ export {};