reamkit 1.22.0 → 1.23.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 (72) hide show
  1. package/dist/esm/core/converter/ream.d.ts +16 -0
  2. package/dist/esm/core/converter/ream.js +59 -9
  3. package/dist/esm/core/document-model/types.d.ts +18 -0
  4. package/dist/esm/core/drawingml/chart-geometry.js +11 -5
  5. package/dist/esm/core/drawingml/colors.d.ts +4 -2
  6. package/dist/esm/core/drawingml/colors.js +15 -0
  7. package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
  8. package/dist/esm/core/drawingml/theme-parser.js +64 -1
  9. package/dist/esm/core/fonts/families.d.ts +10 -0
  10. package/dist/esm/core/fonts/families.js +44 -0
  11. package/dist/esm/core/fonts/index.d.ts +3 -2
  12. package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
  13. package/dist/esm/core/fonts/remote-fonts.js +114 -8
  14. package/dist/esm/core/fonts/scripts.d.ts +26 -0
  15. package/dist/esm/core/fonts/scripts.js +177 -0
  16. package/dist/esm/core/metafile/blit.d.ts +33 -0
  17. package/dist/esm/core/metafile/blit.js +67 -0
  18. package/dist/esm/core/metafile/dib.d.ts +61 -0
  19. package/dist/esm/core/metafile/dib.js +205 -0
  20. package/dist/esm/core/metafile/emf.js +137 -23
  21. package/dist/esm/core/metafile/picture.d.ts +12 -1
  22. package/dist/esm/core/metafile/wmf.js +149 -9
  23. package/dist/esm/core/ole/escher-blip.d.ts +12 -0
  24. package/dist/esm/core/ole/escher-blip.js +88 -0
  25. package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
  26. package/dist/esm/excel/print-model.js +3 -1
  27. package/dist/esm/excel/styles-parser.js +1 -1
  28. package/dist/esm/excel/xls/biff-reader.js +53 -18
  29. package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
  30. package/dist/esm/excel/xls/biff-styles.js +9 -7
  31. package/dist/esm/excel/xls/escher.js +90 -24
  32. package/dist/esm/layout/page-doc.d.ts +39 -0
  33. package/dist/esm/layout/styled-layout.d.ts +2 -2
  34. package/dist/esm/layout/styled-layout.js +301 -35
  35. package/dist/esm/pdf/styled-page-emitter.js +35 -2
  36. package/dist/esm/pdf-reader/image-decode.js +1 -1
  37. package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
  38. package/dist/esm/pptx/embedded-fonts.js +104 -0
  39. package/dist/esm/pptx/placeholder-cascade.d.ts +73 -6
  40. package/dist/esm/pptx/placeholder-cascade.js +114 -18
  41. package/dist/esm/pptx/ppt/ppt-reader.js +120 -10
  42. package/dist/esm/pptx/ppt/ppt-text.d.ts +66 -1
  43. package/dist/esm/pptx/ppt/ppt-text.js +576 -137
  44. package/dist/esm/pptx/pptx-reader.d.ts +8 -0
  45. package/dist/esm/pptx/pptx-reader.js +27 -5
  46. package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
  47. package/dist/esm/pptx/preset-table-styles.js +27 -0
  48. package/dist/esm/pptx/slide-parser.d.ts +7 -1
  49. package/dist/esm/pptx/slide-parser.js +106 -33
  50. package/dist/esm/pptx/sp-helpers.d.ts +2 -1
  51. package/dist/esm/pptx/sp-helpers.js +3 -2
  52. package/dist/esm/pptx/table-style.d.ts +9 -1
  53. package/dist/esm/pptx/table-style.js +49 -7
  54. package/dist/esm/word/document-parser.d.ts +7 -1
  55. package/dist/esm/word/document-parser.js +6 -6
  56. package/dist/esm/word/docx-reader.js +6 -3
  57. package/dist/esm/word/docx-to-pdf.d.ts +8 -7
  58. package/dist/esm/word/docx-to-pdf.js +25 -7
  59. package/dist/esm/word/drawing-parser.js +31 -19
  60. package/dist/esm/word/numbering-parser.d.ts +2 -1
  61. package/dist/esm/word/numbering-parser.js +6 -6
  62. package/dist/esm/word/paragraph-properties.d.ts +2 -1
  63. package/dist/esm/word/paragraph-properties.js +2 -2
  64. package/dist/esm/word/run-properties.d.ts +5 -2
  65. package/dist/esm/word/run-properties.js +13 -8
  66. package/dist/esm/word/styles-parser.d.ts +2 -1
  67. package/dist/esm/word/styles-parser.js +12 -12
  68. package/dist/esm/word/table-parser.js +3 -3
  69. package/dist/esm/word/theme-fonts.d.ts +10 -0
  70. package/dist/esm/word/theme-fonts.js +28 -0
  71. package/package.json +1 -1
  72. /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
@@ -0,0 +1,177 @@
1
+ //#region src/core/fonts/scripts.ts
2
+ var RANGES = [
3
+ [
4
+ 1424,
5
+ 1535,
6
+ "hebrew"
7
+ ],
8
+ [
9
+ 1536,
10
+ 1791,
11
+ "arabic"
12
+ ],
13
+ [
14
+ 1872,
15
+ 1919,
16
+ "arabic"
17
+ ],
18
+ [
19
+ 2208,
20
+ 2303,
21
+ "arabic"
22
+ ],
23
+ [
24
+ 3584,
25
+ 3711,
26
+ "thai"
27
+ ],
28
+ [
29
+ 4352,
30
+ 4607,
31
+ "kr"
32
+ ],
33
+ [
34
+ 8592,
35
+ 8703,
36
+ "symbols"
37
+ ],
38
+ [
39
+ 8960,
40
+ 9215,
41
+ "symbols"
42
+ ],
43
+ [
44
+ 9312,
45
+ 9471,
46
+ "symbols"
47
+ ],
48
+ [
49
+ 9472,
50
+ 10175,
51
+ "symbols"
52
+ ],
53
+ [
54
+ 11008,
55
+ 11263,
56
+ "symbols"
57
+ ],
58
+ [
59
+ 12288,
60
+ 12351,
61
+ "han"
62
+ ],
63
+ [
64
+ 12352,
65
+ 12543,
66
+ "jp"
67
+ ],
68
+ [
69
+ 12592,
70
+ 12687,
71
+ "kr"
72
+ ],
73
+ [
74
+ 13312,
75
+ 19903,
76
+ "han"
77
+ ],
78
+ [
79
+ 19968,
80
+ 40959,
81
+ "han"
82
+ ],
83
+ [
84
+ 44032,
85
+ 55215,
86
+ "kr"
87
+ ],
88
+ [
89
+ 63744,
90
+ 64255,
91
+ "han"
92
+ ],
93
+ [
94
+ 64285,
95
+ 64335,
96
+ "hebrew"
97
+ ],
98
+ [
99
+ 64336,
100
+ 65023,
101
+ "arabic"
102
+ ],
103
+ [
104
+ 65136,
105
+ 65279,
106
+ "arabic"
107
+ ],
108
+ [
109
+ 65280,
110
+ 65519,
111
+ "han"
112
+ ]
113
+ ];
114
+ /**
115
+ * The writing system a character needs a face for, or `undefined` when a Latin
116
+ * family covers it.
117
+ *
118
+ * @param cp The code point.
119
+ * @param hanFace Which face draws unified Han in this document (see
120
+ * {@link scriptsInFlow}); defaults to Simplified.
121
+ */
122
+ function scriptForCodepoint(cp, hanFace = "sc") {
123
+ for (const [lo, hi, key] of RANGES) {
124
+ if (cp < lo) break;
125
+ if (cp <= hi) return key === "han" ? hanFace : key;
126
+ }
127
+ }
128
+ /**
129
+ * The writing systems a document holds text in, beside the Latin one.
130
+ *
131
+ * Unified Han is written the same in Japanese, Korean and Chinese and drawn
132
+ * differently in each, and the character does not say which — the document
133
+ * does: Kana beside it means Japanese, Hangul means Korean. So the whole text
134
+ * is read once, and the answer applies to every Han character in it.
135
+ *
136
+ * @param flow The parsed document.
137
+ * @returns The scripts to fetch a face for, and the one Han is drawn with.
138
+ */
139
+ function scriptsInFlow(flow) {
140
+ const scripts = /* @__PURE__ */ new Set();
141
+ const marks = /* @__PURE__ */ new Set();
142
+ const read = (text) => {
143
+ for (const ch of text) {
144
+ const cp = ch.codePointAt(0) ?? 0;
145
+ if (cp < 1424) continue;
146
+ const key = scriptForCodepoint(cp, "sc");
147
+ if (key === void 0) continue;
148
+ if (key === "sc") {
149
+ marks.add("han");
150
+ continue;
151
+ }
152
+ scripts.add(key);
153
+ }
154
+ };
155
+ const shapeText = (shape) => {
156
+ if (shape.text) visit(shape.text.content);
157
+ for (const child of shape.children ?? []) shapeText(child.shape);
158
+ };
159
+ const visit = (elements) => {
160
+ for (const el of elements) if (el.kind === "paragraph") for (const run of el.paragraph.runs) read(run.text);
161
+ else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
162
+ else if (el.kind === "shape") shapeText(el.shape);
163
+ };
164
+ visit(flow.body);
165
+ for (const band of flow.headersFooters?.values() ?? []) visit(band);
166
+ for (const note of flow.footnotes?.values() ?? []) visit(note);
167
+ for (const note of flow.endnotes?.values() ?? []) visit(note);
168
+ for (const comment of flow.comments?.values() ?? []) visit(comment.content);
169
+ const hanFace = scripts.has("jp") ? "jp" : scripts.has("kr") ? "kr" : "sc";
170
+ if (marks.has("han")) scripts.add(hanFace);
171
+ return {
172
+ scripts,
173
+ hanFace
174
+ };
175
+ }
176
+ //#endregion
177
+ export { scriptForCodepoint, scriptsInFlow };
@@ -0,0 +1,33 @@
1
+ import { DibImage } from './dib.js';
2
+ import { PictureImage } from './picture.js';
3
+ /** How a blit's ROP wants the source drawn. */
4
+ export type BlitMode =
5
+ /** SRCCOPY and its neighbours: the source replaces what is under it. */
6
+ 'opaque'
7
+ /** SRCAND: a mask, held until the picture it belongs to arrives. */
8
+ | 'mask'
9
+ /** SRCPAINT: ORed in, so the source's BLACK leaves the ground showing. */
10
+ | 'or'
11
+ /** A ROP that ignores the source, or one whose result is not a picture. */
12
+ | 'skip';
13
+ /** The destination rectangle a blit paints, in the metafile's own units. */
14
+ export interface BlitRect {
15
+ readonly x: number;
16
+ readonly y: number;
17
+ readonly width: number;
18
+ readonly height: number;
19
+ }
20
+ /**
21
+ * What a ternary raster operation asks a reader to draw.
22
+ *
23
+ * @param rop The ROP code, as the record states it.
24
+ */
25
+ export declare function blitMode(rop: number): BlitMode;
26
+ /**
27
+ * A sink for a metafile's blits that puts the AND/OR pair back together.
28
+ *
29
+ * @param emit Where a finished picture goes — the reader's primitive list.
30
+ */
31
+ export declare function makeBlitter(emit: (prim: PictureImage) => void): {
32
+ blit: (img: DibImage, dest: BlitRect, rop: number) => void;
33
+ };
@@ -0,0 +1,67 @@
1
+ import { dibToPng, maskedDib } from "./dib.js";
2
+ //#region src/core/metafile/blit.ts
3
+ /**
4
+ * What a ternary raster operation asks a reader to draw.
5
+ *
6
+ * @param rop The ROP code, as the record states it.
7
+ */
8
+ function blitMode(rop) {
9
+ switch (rop >>> 0) {
10
+ case 13369376:
11
+ case 14812998: return "opaque";
12
+ case 8913094: return "mask";
13
+ case 15597702: return "or";
14
+ case 6684742:
15
+ case 15728673:
16
+ case 66:
17
+ case 16711778: return "skip";
18
+ default: return (rop >>> 2 & 3342336) !== (rop & 3342336) ? "opaque" : "skip";
19
+ }
20
+ }
21
+ /**
22
+ * A sink for a metafile's blits that puts the AND/OR pair back together.
23
+ *
24
+ * @param emit Where a finished picture goes — the reader's primitive list.
25
+ */
26
+ function makeBlitter(emit) {
27
+ let pending;
28
+ const place = (img, dest) => {
29
+ emit({
30
+ kind: "image",
31
+ ...dest,
32
+ png: dibToPng(img)
33
+ });
34
+ };
35
+ return { blit: (img, dest, rop) => {
36
+ const mode = blitMode(rop);
37
+ if (mode === "skip") return;
38
+ if (mode === "mask") {
39
+ pending = {
40
+ img,
41
+ dest
42
+ };
43
+ return;
44
+ }
45
+ const mask = pending;
46
+ pending = void 0;
47
+ if (mode === "or") {
48
+ place(mask && sameRect(mask.dest, dest) ? maskedDib(img, mask.img) : keyBlack(img), dest);
49
+ return;
50
+ }
51
+ place(img, dest);
52
+ } };
53
+ }
54
+ function sameRect(a, b) {
55
+ return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
56
+ }
57
+ function keyBlack(img) {
58
+ const rgba = new Uint8Array(img.rgba);
59
+ for (let i = 0; i < rgba.length; i += 4) if (rgba[i] === 0 && rgba[i + 1] === 0 && rgba[i + 2] === 0) rgba[i + 3] = 0;
60
+ return {
61
+ width: img.width,
62
+ height: img.height,
63
+ rgba
64
+ };
65
+ }
66
+ //#endregion
67
+ export { makeBlitter };
@@ -0,0 +1,61 @@
1
+ /** A decoded bitmap: straight (un-premultiplied) RGBA, TOP row first. */
2
+ export interface DibImage {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ readonly rgba: Uint8Array;
6
+ /**
7
+ * Whether the source stored its rows bottom-first — which is the usual way,
8
+ * and which a caller needs to know to read a source rectangle: a blit states
9
+ * one in the BITMAP's own coordinates, whose origin is its first row.
10
+ */
11
+ readonly bottomUp?: boolean;
12
+ }
13
+ /** What a caller knows about the bitmap that its own header does not say. */
14
+ export interface DibOptions {
15
+ /**
16
+ * Where the pixels start. EMF states this; a WMF stores the two parts back to
17
+ * back, so leaving it out means "after the palette".
18
+ */
19
+ readonly bitsAt?: number;
20
+ /** How many pixel bytes there are, when the record states it. */
21
+ readonly cbBits?: number;
22
+ /**
23
+ * Whether a 32-bit bitmap's fourth byte is ALPHA. It is reserved in an
24
+ * ordinary blit — GDI ignores it and so must this, or a picture whose
25
+ * producer left it zero comes out invisible — and it is the alpha channel
26
+ * in an EMR_ALPHABLEND that says so.
27
+ */
28
+ readonly alpha?: boolean;
29
+ }
30
+ /**
31
+ * Decode a packed DIB into RGBA.
32
+ *
33
+ * @param bytes The metafile.
34
+ * @param bmiAt Where the bitmap's header starts.
35
+ * @param o What the record says about the bitmap; see {@link DibOptions}.
36
+ * @returns The bitmap, or `undefined` when the header is malformed or its
37
+ * compression is one this decoder does not do.
38
+ */
39
+ export declare function readDib(bytes: Uint8Array, bmiAt: number, o?: DibOptions): DibImage | undefined;
40
+ /**
41
+ * The colour bitmap seen through a monochrome one — the two-blit idiom every
42
+ * clipart of this age is drawn with: an AND of the mask, which knocks the
43
+ * picture's ground to white, and then an OR of the picture, which is black
44
+ * exactly where the ground was. Kept apart they draw a black box; put together
45
+ * they are one bitmap with an alpha channel.
46
+ *
47
+ * @param color The bitmap the second blit paints.
48
+ * @param mask The first blit's mask: WHITE where the picture is to show
49
+ * through, black where it is opaque.
50
+ */
51
+ export declare function maskedDib(color: DibImage, mask: DibImage): DibImage;
52
+ /**
53
+ * The bitmap as a PNG file, ready for the resource store. An opaque one is
54
+ * written without an alpha channel — a quarter less to deflate, and the
55
+ * writers that flatten transparency then have nothing to do.
56
+ */
57
+ export declare function dibToPng(img: DibImage): Uint8Array;
58
+ /** The bitmap at a constant transparency — what a blend function's `SrcConstantAlpha` asks for. */
59
+ export declare function fadedDib(img: DibImage, alpha: number): DibImage;
60
+ /** The part of the bitmap a blit's source rectangle names, or all of it. */
61
+ export declare function cropDib(img: DibImage, x: number, y: number, w: number, h: number): DibImage;
@@ -0,0 +1,205 @@
1
+ import { encodePng } from "../png-encode.js";
2
+ //#region src/core/metafile/dib.ts
3
+ var MAX_PIXELS = 3e7;
4
+ /**
5
+ * Decode a packed DIB into RGBA.
6
+ *
7
+ * @param bytes The metafile.
8
+ * @param bmiAt Where the bitmap's header starts.
9
+ * @param o What the record says about the bitmap; see {@link DibOptions}.
10
+ * @returns The bitmap, or `undefined` when the header is malformed or its
11
+ * compression is one this decoder does not do.
12
+ */
13
+ function readDib(bytes, bmiAt, o = {}) {
14
+ const { bitsAt, cbBits } = o;
15
+ if (bmiAt < 0 || bmiAt + 12 > bytes.length) return void 0;
16
+ const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
17
+ const headerSize = v.getUint32(bmiAt, true);
18
+ const core = headerSize === 12;
19
+ if (!core && (headerSize < 40 || bmiAt + 40 > bytes.length)) return void 0;
20
+ const width = core ? v.getInt16(bmiAt + 4, true) : v.getInt32(bmiAt + 4, true);
21
+ const rawHeight = core ? v.getInt16(bmiAt + 6, true) : v.getInt32(bmiAt + 8, true);
22
+ const bpp = core ? v.getUint16(bmiAt + 10, true) : v.getUint16(bmiAt + 14, true);
23
+ const compression = core ? 0 : v.getUint32(bmiAt + 16, true);
24
+ const topDown = rawHeight < 0;
25
+ const height = Math.abs(rawHeight);
26
+ if (width <= 0 || height <= 0 || width * height > MAX_PIXELS) return void 0;
27
+ if (compression !== 0 && compression !== 3) return void 0;
28
+ if (bpp !== 1 && bpp !== 4 && bpp !== 8 && bpp !== 16 && bpp !== 24 && bpp !== 32) return;
29
+ const paletteAt = bmiAt + headerSize;
30
+ const entry = core ? 3 : 4;
31
+ const declared = core || headerSize < 40 ? 0 : v.getUint32(bmiAt + 32, true);
32
+ const paletteCount = bpp <= 8 ? declared > 0 ? Math.min(declared, 1 << bpp) : 1 << bpp : 0;
33
+ const masksAt = compression === 3 ? paletteAt : void 0;
34
+ const paletteBytes = compression === 3 ? 12 : paletteCount * entry;
35
+ const start = bitsAt ?? paletteAt + paletteBytes;
36
+ const stride = (width * bpp + 7 >> 3) + 3 & -4;
37
+ const need = stride * height;
38
+ if (start < 0 || start + Math.min(need, cbBits ?? need) > bytes.length) return void 0;
39
+ const palette = readPalette(v, paletteAt, paletteCount, entry);
40
+ const channels = masksAt !== void 0 ? [
41
+ maskChannel(v.getUint32(masksAt, true)),
42
+ maskChannel(v.getUint32(masksAt + 4, true)),
43
+ maskChannel(v.getUint32(masksAt + 8, true))
44
+ ] : void 0;
45
+ const rgba = new Uint8Array(width * height * 4);
46
+ for (let y = 0; y < height; y++) {
47
+ const row = start + (topDown ? y : height - 1 - y) * stride;
48
+ let dst = y * width * 4;
49
+ for (let x = 0; x < width; x++) {
50
+ let r = 0;
51
+ let g = 0;
52
+ let b = 0;
53
+ let a = 255;
54
+ if (bpp <= 8) {
55
+ const bitsPer = bpp;
56
+ const bit = x * bitsPer;
57
+ const rgb = palette[(bytes[row + (bit >> 3)] ?? 0) >> 8 - bitsPer - (bit & 7) & (1 << bitsPer) - 1] ?? 0;
58
+ r = rgb >>> 16 & 255;
59
+ g = rgb >>> 8 & 255;
60
+ b = rgb & 255;
61
+ } else if (bpp === 16) {
62
+ const word = (bytes[row + x * 2] ?? 0) | (bytes[row + x * 2 + 1] ?? 0) << 8;
63
+ if (channels) [r, g, b] = [
64
+ channels[0](word),
65
+ channels[1](word),
66
+ channels[2](word)
67
+ ];
68
+ else {
69
+ r = expand5(word >> 10 & 31);
70
+ g = expand5(word >> 5 & 31);
71
+ b = expand5(word & 31);
72
+ }
73
+ } else {
74
+ const at = row + x * (bpp >> 3);
75
+ b = bytes[at] ?? 0;
76
+ g = bytes[at + 1] ?? 0;
77
+ r = bytes[at + 2] ?? 0;
78
+ if (bpp === 32) {
79
+ const word = (b | g << 8 | r << 16 | (bytes[at + 3] ?? 0) << 24) >>> 0 >>> 0;
80
+ if (channels) [r, g, b] = [
81
+ channels[0](word),
82
+ channels[1](word),
83
+ channels[2](word)
84
+ ];
85
+ if (o.alpha === true) {
86
+ a = bytes[at + 3] ?? 0;
87
+ if (a > 0 && a < 255) {
88
+ r = Math.min(255, Math.round(r * 255 / a));
89
+ g = Math.min(255, Math.round(g * 255 / a));
90
+ b = Math.min(255, Math.round(b * 255 / a));
91
+ }
92
+ }
93
+ }
94
+ }
95
+ rgba[dst] = r;
96
+ rgba[dst + 1] = g;
97
+ rgba[dst + 2] = b;
98
+ rgba[dst + 3] = a;
99
+ dst += 4;
100
+ }
101
+ }
102
+ return {
103
+ width,
104
+ height,
105
+ rgba,
106
+ ...topDown ? {} : { bottomUp: true }
107
+ };
108
+ }
109
+ /**
110
+ * The colour bitmap seen through a monochrome one — the two-blit idiom every
111
+ * clipart of this age is drawn with: an AND of the mask, which knocks the
112
+ * picture's ground to white, and then an OR of the picture, which is black
113
+ * exactly where the ground was. Kept apart they draw a black box; put together
114
+ * they are one bitmap with an alpha channel.
115
+ *
116
+ * @param color The bitmap the second blit paints.
117
+ * @param mask The first blit's mask: WHITE where the picture is to show
118
+ * through, black where it is opaque.
119
+ */
120
+ function maskedDib(color, mask) {
121
+ const rgba = new Uint8Array(color.rgba);
122
+ for (let y = 0; y < color.height; y++) {
123
+ const my = Math.min(mask.height - 1, Math.floor(y * mask.height / color.height));
124
+ for (let x = 0; x < color.width; x++) {
125
+ const mx = Math.min(mask.width - 1, Math.floor(x * mask.width / color.width));
126
+ const m = my * mask.width * 4 + mx * 4;
127
+ if ((mask.rgba[m] + mask.rgba[m + 1] + mask.rgba[m + 2]) / 3 >= 128) rgba[(y * color.width + x) * 4 + 3] = 0;
128
+ }
129
+ }
130
+ return {
131
+ width: color.width,
132
+ height: color.height,
133
+ rgba
134
+ };
135
+ }
136
+ /**
137
+ * The bitmap as a PNG file, ready for the resource store. An opaque one is
138
+ * written without an alpha channel — a quarter less to deflate, and the
139
+ * writers that flatten transparency then have nothing to do.
140
+ */
141
+ function dibToPng(img) {
142
+ let opaque = true;
143
+ for (let i = 3; i < img.rgba.length; i += 4) if (img.rgba[i] !== 255) {
144
+ opaque = false;
145
+ break;
146
+ }
147
+ if (!opaque) return encodePng(img.width, img.height, "rgba", img.rgba);
148
+ const rgb = new Uint8Array(img.width * img.height * 3);
149
+ for (let i = 0, j = 0; i < img.rgba.length; i += 4, j += 3) {
150
+ rgb[j] = img.rgba[i];
151
+ rgb[j + 1] = img.rgba[i + 1];
152
+ rgb[j + 2] = img.rgba[i + 2];
153
+ }
154
+ return encodePng(img.width, img.height, "rgb", rgb);
155
+ }
156
+ /** The bitmap at a constant transparency — what a blend function's `SrcConstantAlpha` asks for. */
157
+ function fadedDib(img, alpha) {
158
+ const rgba = new Uint8Array(img.rgba);
159
+ for (let i = 3; i < rgba.length; i += 4) rgba[i] = Math.round(rgba[i] * alpha);
160
+ return {
161
+ width: img.width,
162
+ height: img.height,
163
+ rgba
164
+ };
165
+ }
166
+ /** The part of the bitmap a blit's source rectangle names, or all of it. */
167
+ function cropDib(img, x, y, w, h) {
168
+ const left = Math.max(0, Math.min(img.width - 1, x));
169
+ const top = Math.max(0, Math.min(img.height - 1, y));
170
+ const width = Math.max(1, Math.min(img.width - left, w));
171
+ const height = Math.max(1, Math.min(img.height - top, h));
172
+ if (left === 0 && top === 0 && width === img.width && height === img.height) return img;
173
+ const rgba = new Uint8Array(width * height * 4);
174
+ for (let row = 0; row < height; row++) {
175
+ const src = ((top + row) * img.width + left) * 4;
176
+ rgba.set(img.rgba.subarray(src, src + width * 4), row * width * 4);
177
+ }
178
+ return {
179
+ width,
180
+ height,
181
+ rgba
182
+ };
183
+ }
184
+ function readPalette(v, at, count, entry) {
185
+ const out = new Uint32Array(Math.max(count, 2));
186
+ out[1] = 16777215;
187
+ for (let i = 0; i < count; i++) {
188
+ const o = at + i * entry;
189
+ if (o + 3 > v.byteLength) break;
190
+ out[i] = v.getUint8(o + 2) << 16 | v.getUint8(o + 1) << 8 | v.getUint8(o);
191
+ }
192
+ return out;
193
+ }
194
+ function maskChannel(mask) {
195
+ if (mask === 0) return () => 0;
196
+ let shift = 0;
197
+ while ((mask >>> shift & 1) === 0) shift++;
198
+ const span = (mask >>> shift) + 1;
199
+ return (word) => Math.round(((word & mask) >>> shift) * 255 / (span - 1));
200
+ }
201
+ function expand5(v) {
202
+ return v << 3 | v >> 2;
203
+ }
204
+ //#endregion
205
+ export { cropDib, dibToPng, fadedDib, maskedDib, readDib };