reamkit 1.23.0 → 1.25.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 +22 -11
- package/dist/esm/core/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/converter/facade.d.ts +3 -3
- package/dist/esm/core/converter/facade.js +12 -0
- package/dist/esm/core/converter/ream.d.ts +48 -4
- package/dist/esm/core/converter/ream.js +25 -4
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +54 -0
- package/dist/esm/core/drawingml/chart-geometry.js +3 -2
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.js +9 -3
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- package/dist/esm/core/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- package/dist/esm/core/metafile/emf.js +144 -16
- package/dist/esm/core/metafile/picture.d.ts +49 -0
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +53 -4
- package/dist/esm/core/ole/escher-blip.js +11 -1
- package/dist/esm/core/outline.d.ts +17 -0
- package/dist/esm/core/outline.js +30 -0
- package/dist/esm/core/style-cascade/resolver.js +1 -0
- package/dist/esm/core/style-cascade/types.d.ts +3 -1
- package/dist/esm/excel/print-model.js +2 -2
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.d.ts +10 -0
- package/dist/esm/excel/sheet-to-flow.js +30 -3
- package/dist/esm/html/html-writer.js +5 -3
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +35 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.js +217 -30
- package/dist/esm/markdown/markdown-writer.d.ts +41 -0
- package/dist/esm/markdown/markdown-writer.js +733 -0
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +62 -4
- package/dist/esm/pdf/vector-graphics.js +1 -1
- package/dist/esm/pdf-reader/annots.d.ts +24 -0
- package/dist/esm/pdf-reader/annots.js +126 -0
- package/dist/esm/pdf-reader/content.d.ts +131 -5
- package/dist/esm/pdf-reader/content.js +169 -12
- package/dist/esm/pdf-reader/display.d.ts +56 -0
- package/dist/esm/pdf-reader/display.js +162 -0
- package/dist/esm/pdf-reader/document.d.ts +36 -1
- package/dist/esm/pdf-reader/document.js +92 -25
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +31 -0
- package/dist/esm/pdf-reader/embedded-fonts.js +94 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +61 -6
- package/dist/esm/pdf-reader/flow-build.js +128 -22
- package/dist/esm/pdf-reader/font.js +185 -4
- package/dist/esm/pdf-reader/image-decode.js +56 -5
- package/dist/esm/pdf-reader/images.d.ts +6 -0
- package/dist/esm/pdf-reader/images.js +25 -5
- package/dist/esm/pdf-reader/jpeg.d.ts +18 -0
- package/dist/esm/pdf-reader/jpeg.js +419 -0
- package/dist/esm/pdf-reader/layout.d.ts +1 -1
- package/dist/esm/pdf-reader/layout.js +221 -32
- package/dist/esm/pdf-reader/pattern-tint.d.ts +17 -0
- package/dist/esm/pdf-reader/pattern-tint.js +181 -0
- package/dist/esm/pdf-reader/reader.d.ts +9 -1
- package/dist/esm/pdf-reader/reader.js +22 -6
- package/dist/esm/pdf-reader/shading.d.ts +14 -0
- package/dist/esm/pdf-reader/shading.js +27 -1
- package/dist/esm/pdf-reader/tagged.js +156 -17
- package/dist/esm/pdf-reader/text.d.ts +13 -1
- package/dist/esm/pdf-reader/text.js +70 -3
- package/dist/esm/pdf-reader/vector.d.ts +25 -1
- package/dist/esm/pdf-reader/vector.js +168 -12
- package/dist/esm/pptx/ppt/ppt-reader.js +95 -27
- package/dist/esm/pptx/ppt/ppt-text.d.ts +41 -0
- package/dist/esm/pptx/ppt/ppt-text.js +327 -37
- package/dist/esm/pptx/pptx-reader.js +90 -10
- package/dist/esm/pptx/slide-parser.d.ts +4 -1
- package/dist/esm/pptx/slide-parser.js +104 -13
- package/dist/esm/pptx/sp-helpers.d.ts +7 -3
- package/dist/esm/pptx/sp-helpers.js +45 -7
- package/dist/esm/pptx/table-style.d.ts +7 -0
- package/dist/esm/pptx/table-style.js +59 -7
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +4 -1
- package/dist/esm/word/document-parser.js +4 -1
- package/dist/esm/word/docx-reader.js +26 -13
- package/dist/esm/word/docx-writer.js +15 -1
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +44 -5
- package/dist/esm/word/table-parser.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
//#region src/core/drawingml/text-warp.ts
|
|
2
|
+
/** Distance from the box's centre line, `0` at the middle and `1` at either edge. */
|
|
3
|
+
var fromCentre = (u) => Math.abs(2 * u - 1);
|
|
4
|
+
/** A band of constant height `1 - drop` whose top edge is `top`. */
|
|
5
|
+
var band = (top, drop) => ({
|
|
6
|
+
top,
|
|
7
|
+
bottom: top + (1 - drop)
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* §20.1.10.76 ST_TextShapeType — the warp each preset applies, keyed by its
|
|
11
|
+
* name. `adj` arrives as the `a:avLst` adjustment in hundred-thousandths, or
|
|
12
|
+
* the preset's own default when the file states none.
|
|
13
|
+
*/
|
|
14
|
+
var ENVELOPES = new Map([
|
|
15
|
+
["textPlain", {
|
|
16
|
+
defaultAdj: 0,
|
|
17
|
+
at: () => ({
|
|
18
|
+
top: 0,
|
|
19
|
+
bottom: 1
|
|
20
|
+
})
|
|
21
|
+
}],
|
|
22
|
+
["textStop", {
|
|
23
|
+
defaultAdj: 12500,
|
|
24
|
+
at: (u, adj) => {
|
|
25
|
+
const t = adj * Math.max(0, (fromCentre(u) - .375) / .625);
|
|
26
|
+
return {
|
|
27
|
+
top: t,
|
|
28
|
+
bottom: 1 - t
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}],
|
|
32
|
+
["textTriangle", {
|
|
33
|
+
defaultAdj: 5e4,
|
|
34
|
+
at: (u, adj) => ({
|
|
35
|
+
top: adj * fromCentre(u),
|
|
36
|
+
bottom: 1
|
|
37
|
+
})
|
|
38
|
+
}],
|
|
39
|
+
["textTriangleInverted", {
|
|
40
|
+
defaultAdj: 5e4,
|
|
41
|
+
at: (u, adj) => ({
|
|
42
|
+
top: 0,
|
|
43
|
+
bottom: 1 - adj * fromCentre(u)
|
|
44
|
+
})
|
|
45
|
+
}],
|
|
46
|
+
["textChevron", {
|
|
47
|
+
defaultAdj: 25e3,
|
|
48
|
+
at: (u, adj) => band(adj * fromCentre(u), adj)
|
|
49
|
+
}],
|
|
50
|
+
["textChevronInverted", {
|
|
51
|
+
defaultAdj: 25e3,
|
|
52
|
+
at: (u, adj) => band(adj * (1 - fromCentre(u)), adj)
|
|
53
|
+
}],
|
|
54
|
+
["textCanUp", {
|
|
55
|
+
defaultAdj: 14e3,
|
|
56
|
+
at: (u, adj) => band(adj * fromCentre(u) ** 4, adj)
|
|
57
|
+
}],
|
|
58
|
+
["textCanDown", {
|
|
59
|
+
defaultAdj: 14e3,
|
|
60
|
+
at: (u, adj) => band(adj * (1 - fromCentre(u) ** 4), adj)
|
|
61
|
+
}],
|
|
62
|
+
["textWave1", {
|
|
63
|
+
defaultAdj: 6250,
|
|
64
|
+
at: (u, adj) => band(adj * (1 - Math.sin(2 * Math.PI * u)), 2 * adj)
|
|
65
|
+
}],
|
|
66
|
+
["textWave2", {
|
|
67
|
+
defaultAdj: 6250,
|
|
68
|
+
at: (u, adj) => band(adj * (1 + Math.sin(2 * Math.PI * u)), 2 * adj)
|
|
69
|
+
}],
|
|
70
|
+
["textDoubleWave1", {
|
|
71
|
+
defaultAdj: 6250,
|
|
72
|
+
at: (u, adj) => band(adj * (1 - Math.sin(4 * Math.PI * u)), 2 * adj)
|
|
73
|
+
}],
|
|
74
|
+
["textWave4", {
|
|
75
|
+
defaultAdj: 6250,
|
|
76
|
+
at: (u, adj) => band(adj * (1 + Math.sin(4 * Math.PI * u)), 2 * adj)
|
|
77
|
+
}],
|
|
78
|
+
["textInflate", {
|
|
79
|
+
defaultAdj: 13600,
|
|
80
|
+
at: (u, adj) => {
|
|
81
|
+
const t = adj * fromCentre(u) ** 2;
|
|
82
|
+
return {
|
|
83
|
+
top: t,
|
|
84
|
+
bottom: 1 - t
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}],
|
|
88
|
+
["textDeflate", {
|
|
89
|
+
defaultAdj: 37500,
|
|
90
|
+
at: (u, adj) => {
|
|
91
|
+
const t = adj * (1 - fromCentre(u) ** 2.23);
|
|
92
|
+
return {
|
|
93
|
+
top: t,
|
|
94
|
+
bottom: 1 - t
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}],
|
|
98
|
+
["textInflateTop", {
|
|
99
|
+
defaultAdj: 31800,
|
|
100
|
+
at: (u, adj) => ({
|
|
101
|
+
top: adj * fromCentre(u) ** 2,
|
|
102
|
+
bottom: 1
|
|
103
|
+
})
|
|
104
|
+
}],
|
|
105
|
+
["textDeflateTop", {
|
|
106
|
+
defaultAdj: 47e3,
|
|
107
|
+
at: (u, adj) => ({
|
|
108
|
+
top: adj * (1 - fromCentre(u) ** 2),
|
|
109
|
+
bottom: 1
|
|
110
|
+
})
|
|
111
|
+
}],
|
|
112
|
+
["textInflateBottom", {
|
|
113
|
+
defaultAdj: 31800,
|
|
114
|
+
at: (u, adj) => ({
|
|
115
|
+
top: 0,
|
|
116
|
+
bottom: 1 - adj * fromCentre(u) ** 2
|
|
117
|
+
})
|
|
118
|
+
}],
|
|
119
|
+
["textDeflateBottom", {
|
|
120
|
+
defaultAdj: 47e3,
|
|
121
|
+
at: (u, adj) => ({
|
|
122
|
+
top: 0,
|
|
123
|
+
bottom: 1 - adj * (1 - fromCentre(u) ** 2)
|
|
124
|
+
})
|
|
125
|
+
}],
|
|
126
|
+
["textDeflateInflate", {
|
|
127
|
+
defaultAdj: 19800,
|
|
128
|
+
at: (u, adj) => ({
|
|
129
|
+
top: 0,
|
|
130
|
+
bottom: .268 + adj * fromCentre(u) ** 2
|
|
131
|
+
})
|
|
132
|
+
}],
|
|
133
|
+
["textDeflateInflateDeflate", {
|
|
134
|
+
defaultAdj: 4600,
|
|
135
|
+
at: (u, adj) => ({
|
|
136
|
+
top: 0,
|
|
137
|
+
bottom: .258 + adj * fromCentre(u) ** 2
|
|
138
|
+
})
|
|
139
|
+
}],
|
|
140
|
+
["textFadeRight", {
|
|
141
|
+
defaultAdj: 33333,
|
|
142
|
+
at: (u, adj) => ({
|
|
143
|
+
top: adj * u,
|
|
144
|
+
bottom: 1 - adj * u
|
|
145
|
+
})
|
|
146
|
+
}],
|
|
147
|
+
["textFadeLeft", {
|
|
148
|
+
defaultAdj: 33333,
|
|
149
|
+
at: (u, adj) => ({
|
|
150
|
+
top: adj * (1 - u),
|
|
151
|
+
bottom: 1 - adj * (1 - u)
|
|
152
|
+
})
|
|
153
|
+
}],
|
|
154
|
+
["textFadeUp", {
|
|
155
|
+
defaultAdj: 1e5,
|
|
156
|
+
at: (u, adj) => ({
|
|
157
|
+
top: adj * fromCentre(u) ** 2,
|
|
158
|
+
bottom: 1
|
|
159
|
+
})
|
|
160
|
+
}],
|
|
161
|
+
["textFadeDown", {
|
|
162
|
+
defaultAdj: 1e5,
|
|
163
|
+
at: (u, adj) => ({
|
|
164
|
+
top: 0,
|
|
165
|
+
bottom: 1 - adj * fromCentre(u) ** 2
|
|
166
|
+
})
|
|
167
|
+
}],
|
|
168
|
+
["textSlantUp", {
|
|
169
|
+
defaultAdj: 55500,
|
|
170
|
+
at: (u, adj) => band(adj * (1 - u), adj)
|
|
171
|
+
}],
|
|
172
|
+
["textSlantDown", {
|
|
173
|
+
defaultAdj: 44400,
|
|
174
|
+
at: (u, adj) => band(adj * u, adj)
|
|
175
|
+
}],
|
|
176
|
+
["textCascadeUp", {
|
|
177
|
+
defaultAdj: 55500,
|
|
178
|
+
at: (u, adj) => ({
|
|
179
|
+
top: .138 * (1 - u),
|
|
180
|
+
bottom: 1 - adj + adj * (1 - u)
|
|
181
|
+
})
|
|
182
|
+
}],
|
|
183
|
+
["textCascadeDown", {
|
|
184
|
+
defaultAdj: 55500,
|
|
185
|
+
at: (u, adj) => ({
|
|
186
|
+
top: .138 * u,
|
|
187
|
+
bottom: 1 - adj + adj * u
|
|
188
|
+
})
|
|
189
|
+
}],
|
|
190
|
+
["textCurveUp", {
|
|
191
|
+
defaultAdj: 45600,
|
|
192
|
+
at: (u, adj) => curve(1 - u, adj)
|
|
193
|
+
}],
|
|
194
|
+
["textCurveDown", {
|
|
195
|
+
defaultAdj: 45600,
|
|
196
|
+
at: (u, adj) => curve(u, adj)
|
|
197
|
+
}]
|
|
198
|
+
]);
|
|
199
|
+
/**
|
|
200
|
+
* The ring presets, which do not hang text between two edges at all: they wind
|
|
201
|
+
* it once round an ellipse inscribed in the box. Both start at the box's left
|
|
202
|
+
* edge; `Inside` climbs over the top with each letter's head pointing out of
|
|
203
|
+
* the ring, `Outside` dips under the bottom with each letter's head pointing
|
|
204
|
+
* into it — which is why half of either one reads upside down.
|
|
205
|
+
*
|
|
206
|
+
* The band the letters occupy is a fixed share of the ring whatever the text:
|
|
207
|
+
* eight letters and thirty-two fill exactly the same annulus, so it is the
|
|
208
|
+
* preset and not the string that fixes the inner radius.
|
|
209
|
+
*/
|
|
210
|
+
var RING_INNER = .34;
|
|
211
|
+
var RINGS = new Map([["textRingInside", {
|
|
212
|
+
clockwise: true,
|
|
213
|
+
headOut: true
|
|
214
|
+
}], ["textRingOutside", {
|
|
215
|
+
clockwise: false,
|
|
216
|
+
headOut: false
|
|
217
|
+
}]]);
|
|
218
|
+
/**
|
|
219
|
+
* The `textCurve*` pair: the baseline is an arc over the box and the band
|
|
220
|
+
* narrows towards the end the text runs to.
|
|
221
|
+
*
|
|
222
|
+
* @param u Position across the box, already turned the way the preset runs.
|
|
223
|
+
* @param adj How much of the band's height the taper takes by the far end.
|
|
224
|
+
* @returns The two edges at `u`.
|
|
225
|
+
*/
|
|
226
|
+
function curve(u, adj) {
|
|
227
|
+
const bottom = 1 - .24 * (2 * u - 1) ** 2;
|
|
228
|
+
return {
|
|
229
|
+
top: bottom - (.758 - adj * u ** 1.7),
|
|
230
|
+
bottom
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Whether a preset bends text at all. `textNoShape` is the "no warp" member of
|
|
235
|
+
* the enumeration and by far its most common value — two thirds of the decks
|
|
236
|
+
* that mention a warp state only this one — and a body carrying it is an
|
|
237
|
+
* ordinary text box, NOT WordArt stretched to its shape.
|
|
238
|
+
*
|
|
239
|
+
* @param preset The `a:prstTxWarp @prst` value.
|
|
240
|
+
* @returns Whether {@link textWarpEdges} can bend text through this preset.
|
|
241
|
+
*/
|
|
242
|
+
function isTextWarp(preset) {
|
|
243
|
+
return ENVELOPES.has(preset) || RINGS.has(preset);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The matrix that places one glyph of a warped body.
|
|
247
|
+
*
|
|
248
|
+
* Maps the glyph's own text space — x along its advance, y UP from its
|
|
249
|
+
* baseline — into the page's top-left frame, as `[a, b, c, d, e, f]` with
|
|
250
|
+
* `px = a·x + c·y + e` and `py = b·x + d·y + f`. Upright text therefore has a
|
|
251
|
+
* negative `d`: the glyph's y runs against the page's.
|
|
252
|
+
*
|
|
253
|
+
* @param frame The two frames, from the laid-out line.
|
|
254
|
+
* @param glyphX The glyph's left edge in the un-warped block.
|
|
255
|
+
* @param advance How wide the glyph is there.
|
|
256
|
+
* @param baselineY The line's baseline in the un-warped block.
|
|
257
|
+
* @returns The six numbers, or `undefined` where the warp leaves no room to
|
|
258
|
+
* draw — the pinched end of a fade, or a preset this file cannot bend.
|
|
259
|
+
*/
|
|
260
|
+
function warpGlyphMatrix(frame, glyphX, advance, baselineY) {
|
|
261
|
+
const u = (glyphX + advance / 2 - frame.srcX) / frame.srcWidth;
|
|
262
|
+
const v = (baselineY - frame.srcTop) / frame.srcHeight;
|
|
263
|
+
const ring = RINGS.get(frame.preset);
|
|
264
|
+
if (ring) return ringMatrix(frame, ring, u, v, glyphX);
|
|
265
|
+
return envelopeMatrix(frame, u, v, glyphX, advance);
|
|
266
|
+
}
|
|
267
|
+
/** The glyph matrix for a warp that hangs text between a top and a bottom edge. */
|
|
268
|
+
function envelopeMatrix(frame, u, v, glyphX, advance) {
|
|
269
|
+
const edges = textWarpEdges(frame.preset, u, frame.adjust);
|
|
270
|
+
if (!edges) return void 0;
|
|
271
|
+
const height = edges.bottom - edges.top;
|
|
272
|
+
if (!(height > 0)) return void 0;
|
|
273
|
+
const sx = frame.boxWidth / frame.srcWidth;
|
|
274
|
+
const sy = height * frame.boxHeight / frame.srcHeight;
|
|
275
|
+
const step = .004;
|
|
276
|
+
const lo = Math.max(0, u - step);
|
|
277
|
+
const hi = Math.min(1, u + step);
|
|
278
|
+
const at = (q) => {
|
|
279
|
+
const e = textWarpEdges(frame.preset, q, frame.adjust);
|
|
280
|
+
return e ? e.top + v * (e.bottom - e.top) : void 0;
|
|
281
|
+
};
|
|
282
|
+
const before = at(lo);
|
|
283
|
+
const after = at(hi);
|
|
284
|
+
const slope = before !== void 0 && after !== void 0 && hi > lo ? (after - before) * frame.boxHeight / ((hi - lo) * frame.boxWidth) : 0;
|
|
285
|
+
const x0 = frame.boxX + (glyphX - frame.srcX) / frame.srcWidth * frame.boxWidth;
|
|
286
|
+
const mid = edges.top + v * height;
|
|
287
|
+
const y0 = frame.boxY + mid * frame.boxHeight - slope * advance * sx / 2;
|
|
288
|
+
return [
|
|
289
|
+
sx,
|
|
290
|
+
slope * sx,
|
|
291
|
+
0,
|
|
292
|
+
-sy,
|
|
293
|
+
x0,
|
|
294
|
+
y0
|
|
295
|
+
];
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* The glyph matrix for a ring: one turn round the ellipse inscribed in the box.
|
|
299
|
+
*
|
|
300
|
+
* The band is a CONSTANT inset from that ellipse, not a scaled copy of it. A
|
|
301
|
+
* scaled copy would be the natural reading — a circular annulus squashed to the
|
|
302
|
+
* box's proportions — but it makes the band four times deeper at the ring's
|
|
303
|
+
* sides than at its top on a box four times wider than tall, and the letters
|
|
304
|
+
* there come out as spikes radiating from the centre. Held at a constant depth
|
|
305
|
+
* the ring's outer edge still touches the box all the way round, which is what
|
|
306
|
+
* both references draw and what a box of capitals measures.
|
|
307
|
+
*/
|
|
308
|
+
function ringMatrix(frame, ring, _u, v, glyphX) {
|
|
309
|
+
const a = frame.boxWidth / 2;
|
|
310
|
+
const b = frame.boxHeight / 2;
|
|
311
|
+
if (!(a > 0 && b > 0 && frame.srcWidth > 0 && frame.srcHeight > 0)) return void 0;
|
|
312
|
+
const uLeft = (glyphX - frame.srcX) / frame.srcWidth;
|
|
313
|
+
const turn = ring.clockwise ? -1 : 1;
|
|
314
|
+
const psi = Math.PI + turn * 2 * Math.PI * uLeft;
|
|
315
|
+
const cos = Math.cos(psi);
|
|
316
|
+
const sin = Math.sin(psi);
|
|
317
|
+
const rawX = -turn * a * sin;
|
|
318
|
+
const rawY = -turn * b * cos;
|
|
319
|
+
const speed = Math.hypot(rawX, rawY);
|
|
320
|
+
if (!(speed > 0)) return void 0;
|
|
321
|
+
const tx = rawX / speed;
|
|
322
|
+
const ty = rawY / speed;
|
|
323
|
+
const inX = -turn * -ty;
|
|
324
|
+
const inY = -turn * tx;
|
|
325
|
+
const head = ring.headOut ? -1 : 1;
|
|
326
|
+
const depth = (1 - RING_INNER) * b;
|
|
327
|
+
const sT = 2 * Math.PI * speed / frame.srcWidth;
|
|
328
|
+
const sN = depth / frame.srcHeight;
|
|
329
|
+
const inset = (ring.headOut ? v : 1 - v) * depth;
|
|
330
|
+
return [
|
|
331
|
+
sT * tx,
|
|
332
|
+
sT * ty,
|
|
333
|
+
sN * head * inX,
|
|
334
|
+
sN * head * inY,
|
|
335
|
+
frame.boxX + a + a * cos + inX * inset,
|
|
336
|
+
frame.boxY + b - b * sin + inY * inset
|
|
337
|
+
];
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* The two edges of a warp at one position across its box.
|
|
341
|
+
*
|
|
342
|
+
* @param preset The `a:prstTxWarp @prst` value.
|
|
343
|
+
* @param u Position across the box, `0` at its left edge and `1` at its right.
|
|
344
|
+
* @param adj The `a:avLst` `adj` guide in hundred-thousandths, when the file states one.
|
|
345
|
+
* @returns The top and bottom edges, `0`…`1` down the box, or `undefined` for a
|
|
346
|
+
* preset this file does not bend (an unknown name, or `textNoShape`).
|
|
347
|
+
*/
|
|
348
|
+
function textWarpEdges(preset, u, adj) {
|
|
349
|
+
const env = ENVELOPES.get(preset);
|
|
350
|
+
if (!env) return void 0;
|
|
351
|
+
const a = (adj ?? env.defaultAdj) / 1e5;
|
|
352
|
+
return env.at(Math.min(1, Math.max(0, u)), a);
|
|
353
|
+
}
|
|
354
|
+
//#endregion
|
|
355
|
+
export { isTextWarp, warpGlyphMatrix };
|
|
@@ -168,7 +168,13 @@ function parseThemeFillStyles(themeXml) {
|
|
|
168
168
|
"a:fmtScheme",
|
|
169
169
|
"a:fillStyleLst"
|
|
170
170
|
]);
|
|
171
|
-
return list ?
|
|
171
|
+
return list ? fillsOf(list) : [];
|
|
172
|
+
}
|
|
173
|
+
function fillsOf(list) {
|
|
174
|
+
return poChildren(list).filter((c) => {
|
|
175
|
+
const tag = poTag(c);
|
|
176
|
+
return tag !== void 0 && tag !== "#text";
|
|
177
|
+
});
|
|
172
178
|
}
|
|
173
179
|
/**
|
|
174
180
|
* Parse a theme's BACKGROUND fill styles (§20.1.4.1.7 `a:bgFillStyleLst`), as
|
|
@@ -186,7 +192,7 @@ function parseThemeBgFillStyles(themeXml) {
|
|
|
186
192
|
"a:fmtScheme",
|
|
187
193
|
"a:bgFillStyleLst"
|
|
188
194
|
]);
|
|
189
|
-
return list ?
|
|
195
|
+
return list ? fillsOf(list) : [];
|
|
190
196
|
}
|
|
191
197
|
/**
|
|
192
198
|
* Parse a theme's effect styles (§20.1.4.1.15 `a:effectStyleLst`), as the raw
|
|
@@ -204,7 +210,7 @@ function parseThemeEffectStyles(themeXml) {
|
|
|
204
210
|
"a:fmtScheme",
|
|
205
211
|
"a:effectStyleLst"
|
|
206
212
|
]);
|
|
207
|
-
return list ?
|
|
213
|
+
return list ? fillsOf(list) : [];
|
|
208
214
|
}
|
|
209
215
|
var EMU_PER_POINT = 12700;
|
|
210
216
|
function colorOf(slot) {
|
|
@@ -10,6 +10,18 @@ export interface FontMeasure {
|
|
|
10
10
|
readonly pdfWidthForGid: (gid: number) => number;
|
|
11
11
|
/** Shaped width of `text` at `fontSize`, in points. */
|
|
12
12
|
readonly textWidthPt: (text: string, fontSize: number) => number;
|
|
13
|
+
/**
|
|
14
|
+
* How far this text's INK actually reaches from its baseline, in points:
|
|
15
|
+
* `above` up to its highest glyph edge, `below` down to its lowest. Not the
|
|
16
|
+
* font's line box — "HHH" reaches a cap height and no further, and only a
|
|
17
|
+
* string carrying a descender reaches under the baseline at all. WordArt
|
|
18
|
+
* stretches the ink it draws to fill its shape (§20.1.9.10), so it is the
|
|
19
|
+
* ink and not the line box that has to be measured.
|
|
20
|
+
*/
|
|
21
|
+
readonly textInkPt: (text: string, fontSize: number) => {
|
|
22
|
+
above: number;
|
|
23
|
+
below: number;
|
|
24
|
+
};
|
|
13
25
|
/** Encode `text` as a hex string of 4-digit glyph ids (Identity-H addressing). */
|
|
14
26
|
readonly encodeTextAsCidHex: (text: string) => string;
|
|
15
27
|
/**
|
|
@@ -52,9 +52,45 @@ function createFontMeasure(parsed, kern = true) {
|
|
|
52
52
|
if (hex !== "") parts.push(`<${hex}>`);
|
|
53
53
|
return `[${parts.join(" ")}] TJ`;
|
|
54
54
|
};
|
|
55
|
+
const glyf = parsed.tables.get("glyf");
|
|
56
|
+
const glyphInk = (gid) => {
|
|
57
|
+
if (!glyf || gid < 0 || gid >= parsed.numGlyphs) return void 0;
|
|
58
|
+
const start = parsed.glyphOffsets[gid] ?? 0;
|
|
59
|
+
if ((parsed.glyphOffsets[gid + 1] ?? 0) - start < 10) return void 0;
|
|
60
|
+
const at = glyf.offset + start;
|
|
61
|
+
const i16 = (o) => {
|
|
62
|
+
const v = (parsed.raw[at + o] ?? 0) << 8 | (parsed.raw[at + o + 1] ?? 0);
|
|
63
|
+
return v >= 32768 ? v - 65536 : v;
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
yMin: i16(4),
|
|
67
|
+
yMax: i16(8)
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
const textInkPt = (text, fontSize) => {
|
|
71
|
+
const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, kerning, parsed.joiningForms);
|
|
72
|
+
let yMax = -Infinity;
|
|
73
|
+
let yMin = Infinity;
|
|
74
|
+
for (const gid of shaped.gids) {
|
|
75
|
+
const ink = glyphInk(gid);
|
|
76
|
+
if (!ink) continue;
|
|
77
|
+
if (ink.yMax > yMax) yMax = ink.yMax;
|
|
78
|
+
if (ink.yMin < yMin) yMin = ink.yMin;
|
|
79
|
+
}
|
|
80
|
+
if (!Number.isFinite(yMax) || !Number.isFinite(yMin)) {
|
|
81
|
+
yMax = parsed.capHeight || parsed.ascender;
|
|
82
|
+
yMin = 0;
|
|
83
|
+
}
|
|
84
|
+
const k = fontSize / parsed.unitsPerEm;
|
|
85
|
+
return {
|
|
86
|
+
above: yMax * k,
|
|
87
|
+
below: -yMin * k
|
|
88
|
+
};
|
|
89
|
+
};
|
|
55
90
|
return {
|
|
56
91
|
pdfWidthForGid,
|
|
57
92
|
textWidthPt,
|
|
93
|
+
textInkPt,
|
|
58
94
|
encodeTextAsCidHex,
|
|
59
95
|
showText
|
|
60
96
|
};
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
/** The raster formats this module recognizes and can prepare for embedding. */
|
|
2
|
-
export type ImageFormat = 'jpeg' | 'png' | 'jpeg2000' | 'gif' | 'tiff';
|
|
2
|
+
export type ImageFormat = 'jpeg' | 'png' | 'jpeg2000' | 'gif' | 'tiff' | 'bmp';
|
|
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
|
|
6
6
|
* {@link ImageFormat}, or `null` when none matches.
|
|
7
7
|
*/
|
|
8
8
|
export declare function detectImageFormat(bytes: Uint8Array): ImageFormat | null;
|
|
9
|
+
/**
|
|
10
|
+
* The same picture with one colour knocked out of it, as a PNG.
|
|
11
|
+
*
|
|
12
|
+
* A picture may name a colour it is drawn WITHOUT — MS-ODRAW's
|
|
13
|
+
* `pictureTransparent`, DrawingML's `a:clrChange` to nothing — which is how
|
|
14
|
+
* clip art of the pre-alpha age says "this rectangle of ground is not part of
|
|
15
|
+
* the drawing". It is a property of the USE, not of the file, so it is baked
|
|
16
|
+
* here into bytes of its own: two shapes may knock different colours out of the
|
|
17
|
+
* same blip, and a resource store that hashes content then keeps them apart by
|
|
18
|
+
* itself.
|
|
19
|
+
*
|
|
20
|
+
* 23884's satellite sits on a red field and its globe on a white one; drawn as
|
|
21
|
+
* stored, they are a red block and a white square on a blue slide.
|
|
22
|
+
*
|
|
23
|
+
* @param bytes The picture, in any format {@link prepareImage} reads.
|
|
24
|
+
* @param hex The colour to knock out, 6-hex and no leading `#`.
|
|
25
|
+
* @returns A PNG with that colour transparent, or `undefined` when the picture
|
|
26
|
+
* cannot be decoded to samples this can work on.
|
|
27
|
+
*/
|
|
28
|
+
export declare function knockOutColor(bytes: Uint8Array, hex: string): Uint8Array | undefined;
|
|
9
29
|
/** Options controlling how {@link prepareImage} emits an image. */
|
|
10
30
|
export interface EmbedImageOptions {
|
|
11
31
|
/**
|
|
@@ -24,7 +44,7 @@ export interface EmbedImageOptions {
|
|
|
24
44
|
*/
|
|
25
45
|
export interface PreparedImage {
|
|
26
46
|
readonly format: ImageFormat;
|
|
27
|
-
readonly mimeType: 'image/jp2' | 'image/jpeg' | 'image/png' | 'image/gif' | 'image/tiff';
|
|
47
|
+
readonly mimeType: 'image/jp2' | 'image/jpeg' | 'image/png' | 'image/gif' | 'image/tiff' | 'image/bmp';
|
|
28
48
|
readonly widthPx: number;
|
|
29
49
|
readonly heightPx: number;
|
|
30
50
|
/**
|
|
@@ -38,6 +58,14 @@ export interface PreparedImage {
|
|
|
38
58
|
readonly data: Uint8Array;
|
|
39
59
|
/** PNG alpha channel, already FlateDecode-compressed (DeviceGray, 8 bpc). */
|
|
40
60
|
readonly smaskData?: Uint8Array;
|
|
61
|
+
/**
|
|
62
|
+
* The resolution the picture states for itself, in pixels per inch, when it
|
|
63
|
+
* states one (JFIF's `Xdensity`/`Ydensity`, PNG's `pHYs`). It is what makes a
|
|
64
|
+
* picture's NATURAL size: 800 pixels at 300 dpi is 192 points wide, not the
|
|
65
|
+
* 600 the 96-dpi default would give. Absent ⇒ the reader's own default.
|
|
66
|
+
*/
|
|
67
|
+
readonly dpiX?: number;
|
|
68
|
+
readonly dpiY?: number;
|
|
41
69
|
}
|
|
42
70
|
/**
|
|
43
71
|
* Decode and validate one image into a {@link PreparedImage} ready to embed.
|
package/dist/esm/core/images.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { decodeBmp, isBmp } from "./bmp.js";
|
|
2
|
+
import { encodePng } from "./png-encode.js";
|
|
1
3
|
import { decodeTiff, isTiff } from "./tiff.js";
|
|
2
4
|
import { unzlibSync, zlibSync } from "fflate";
|
|
3
5
|
//#region src/core/images.ts
|
|
@@ -13,9 +15,61 @@ function detectImageFormat(bytes) {
|
|
|
13
15
|
if (bytes.length >= 4 && bytes[0] === 255 && bytes[1] === 79 && bytes[2] === 255 && bytes[3] === 81) return "jpeg2000";
|
|
14
16
|
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
17
|
if (isTiff(bytes)) return "tiff";
|
|
18
|
+
if (isBmp(bytes)) return "bmp";
|
|
16
19
|
return null;
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
22
|
+
* The same picture with one colour knocked out of it, as a PNG.
|
|
23
|
+
*
|
|
24
|
+
* A picture may name a colour it is drawn WITHOUT — MS-ODRAW's
|
|
25
|
+
* `pictureTransparent`, DrawingML's `a:clrChange` to nothing — which is how
|
|
26
|
+
* clip art of the pre-alpha age says "this rectangle of ground is not part of
|
|
27
|
+
* the drawing". It is a property of the USE, not of the file, so it is baked
|
|
28
|
+
* here into bytes of its own: two shapes may knock different colours out of the
|
|
29
|
+
* same blip, and a resource store that hashes content then keeps them apart by
|
|
30
|
+
* itself.
|
|
31
|
+
*
|
|
32
|
+
* 23884's satellite sits on a red field and its globe on a white one; drawn as
|
|
33
|
+
* stored, they are a red block and a white square on a blue slide.
|
|
34
|
+
*
|
|
35
|
+
* @param bytes The picture, in any format {@link prepareImage} reads.
|
|
36
|
+
* @param hex The colour to knock out, 6-hex and no leading `#`.
|
|
37
|
+
* @returns A PNG with that colour transparent, or `undefined` when the picture
|
|
38
|
+
* cannot be decoded to samples this can work on.
|
|
39
|
+
*/
|
|
40
|
+
function knockOutColor(bytes, hex) {
|
|
41
|
+
let prepared;
|
|
42
|
+
try {
|
|
43
|
+
prepared = prepareImage(bytes);
|
|
44
|
+
} catch {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (prepared.filter !== "FlateDecode" || prepared.bitsPerComponent !== 8) return void 0;
|
|
48
|
+
const key = [
|
|
49
|
+
0,
|
|
50
|
+
2,
|
|
51
|
+
4
|
|
52
|
+
].map((i) => Number.parseInt(hex.slice(i, i + 2), 16));
|
|
53
|
+
if (key.some((c) => !Number.isFinite(c))) return void 0;
|
|
54
|
+
const gray = prepared.colorSpace === "DeviceGray";
|
|
55
|
+
const src = unzlibSync(prepared.data);
|
|
56
|
+
const existing = prepared.smaskData ? unzlibSync(prepared.smaskData) : void 0;
|
|
57
|
+
const { widthPx: w, heightPx: h } = prepared;
|
|
58
|
+
const channels = gray ? 1 : 3;
|
|
59
|
+
if (src.length < w * h * channels) return void 0;
|
|
60
|
+
const out = new Uint8Array(w * h * 4);
|
|
61
|
+
for (let i = 0; i < w * h; i++) {
|
|
62
|
+
const r = gray ? src[i] : src[i * 3];
|
|
63
|
+
const g = gray ? src[i] : src[i * 3 + 1];
|
|
64
|
+
const b = gray ? src[i] : src[i * 3 + 2];
|
|
65
|
+
out[i * 4] = r;
|
|
66
|
+
out[i * 4 + 1] = g;
|
|
67
|
+
out[i * 4 + 2] = b;
|
|
68
|
+
out[i * 4 + 3] = r === key[0] && g === key[1] && b === key[2] ? 0 : existing?.[i] ?? 255;
|
|
69
|
+
}
|
|
70
|
+
return encodePng(w, h, "rgba", out);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
19
73
|
* Decode and validate one image into a {@link PreparedImage} ready to embed.
|
|
20
74
|
* JPEG and JPEG 2000 pass through verbatim (readers decode them); PNG is
|
|
21
75
|
* inflated, de-filtered and re-compressed, splitting any alpha into a soft mask.
|
|
@@ -29,6 +83,7 @@ function prepareImage(bytes, options = {}) {
|
|
|
29
83
|
if (format === "jpeg2000") return prepareJpeg2000(bytes);
|
|
30
84
|
if (format === "gif") return prepareGif(bytes, options);
|
|
31
85
|
if (format === "tiff") return prepareTiff(bytes, options);
|
|
86
|
+
if (format === "bmp") return prepareBmp(bytes, options);
|
|
32
87
|
throw new Error("Unsupported image format");
|
|
33
88
|
}
|
|
34
89
|
function prepareJpeg2000(bytes) {
|
|
@@ -91,6 +146,7 @@ function readSiz(bytes, sizOffset) {
|
|
|
91
146
|
}
|
|
92
147
|
function prepareJpeg(bytes) {
|
|
93
148
|
const info = readJpegInfo(bytes);
|
|
149
|
+
const density = readJfifDensity(bytes);
|
|
94
150
|
return {
|
|
95
151
|
format: "jpeg",
|
|
96
152
|
mimeType: "image/jpeg",
|
|
@@ -99,7 +155,24 @@ function prepareJpeg(bytes) {
|
|
|
99
155
|
colorSpace: info.numComponents === 1 ? "DeviceGray" : "DeviceRGB",
|
|
100
156
|
bitsPerComponent: info.precision,
|
|
101
157
|
filter: "DCTDecode",
|
|
102
|
-
data: bytes
|
|
158
|
+
data: bytes,
|
|
159
|
+
...density ?? {}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function readJfifDensity(bytes) {
|
|
163
|
+
if (bytes.length < 20 || bytes[2] !== 255 || bytes[3] !== 224) return void 0;
|
|
164
|
+
if (!(bytes[6] === 74 && bytes[7] === 70 && bytes[8] === 73 && bytes[9] === 70)) return void 0;
|
|
165
|
+
const units = bytes[13];
|
|
166
|
+
const x = bytes[14] << 8 | bytes[15];
|
|
167
|
+
const y = bytes[16] << 8 | bytes[17];
|
|
168
|
+
if (x <= 0 || y <= 0) return void 0;
|
|
169
|
+
if (units === 1) return {
|
|
170
|
+
dpiX: x,
|
|
171
|
+
dpiY: y
|
|
172
|
+
};
|
|
173
|
+
if (units === 2) return {
|
|
174
|
+
dpiX: x * 2.54,
|
|
175
|
+
dpiY: y * 2.54
|
|
103
176
|
};
|
|
104
177
|
}
|
|
105
178
|
function readJpegInfo(bytes) {
|
|
@@ -198,6 +271,34 @@ function prepareTiff(bytes, options = {}) {
|
|
|
198
271
|
...alpha && !options.flattenAlpha ? { smaskData: zlibSync(alpha.slice()) } : {}
|
|
199
272
|
};
|
|
200
273
|
}
|
|
274
|
+
function prepareBmp(bytes, options = {}) {
|
|
275
|
+
const img = decodeBmp(bytes);
|
|
276
|
+
const data = img.data.slice();
|
|
277
|
+
const alpha = img.alpha;
|
|
278
|
+
if (alpha && options.flattenAlpha) for (let i = 0; i < alpha.length; i++) {
|
|
279
|
+
if (alpha[i] === 255) continue;
|
|
280
|
+
const a = alpha[i] / 255;
|
|
281
|
+
for (let c = 0; c < 3; c++) {
|
|
282
|
+
const at = i * 3 + c;
|
|
283
|
+
data[at] = Math.round(data[at] * a + 255 * (1 - a));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
format: "bmp",
|
|
288
|
+
mimeType: "image/bmp",
|
|
289
|
+
widthPx: img.width,
|
|
290
|
+
heightPx: img.height,
|
|
291
|
+
colorSpace: "DeviceRGB",
|
|
292
|
+
bitsPerComponent: 8,
|
|
293
|
+
filter: "FlateDecode",
|
|
294
|
+
data: zlibSync(data),
|
|
295
|
+
...alpha && !options.flattenAlpha ? { smaskData: zlibSync(alpha.slice()) } : {},
|
|
296
|
+
...img.dpiX !== void 0 && img.dpiY !== void 0 ? {
|
|
297
|
+
dpiX: img.dpiX,
|
|
298
|
+
dpiY: img.dpiY
|
|
299
|
+
} : {}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
201
302
|
function cmykToRgb(cmyk) {
|
|
202
303
|
const out = new Uint8Array(cmyk.length / 4 * 3);
|
|
203
304
|
for (let i = 0; i * 4 + 3 < cmyk.length; i++) {
|
|
@@ -356,7 +457,11 @@ function preparePng(bytes, options = {}) {
|
|
|
356
457
|
bitsPerComponent: decoded.bitsPerComponent,
|
|
357
458
|
filter: "FlateDecode",
|
|
358
459
|
data: zlibSync(decoded.raw),
|
|
359
|
-
...decoded.smaskRaw ? { smaskData: zlibSync(decoded.smaskRaw) } : {}
|
|
460
|
+
...decoded.smaskRaw ? { smaskData: zlibSync(decoded.smaskRaw) } : {},
|
|
461
|
+
...decoded.dpiX !== void 0 && decoded.dpiY !== void 0 ? {
|
|
462
|
+
dpiX: decoded.dpiX,
|
|
463
|
+
dpiY: decoded.dpiY
|
|
464
|
+
} : {}
|
|
360
465
|
};
|
|
361
466
|
}
|
|
362
467
|
function decodePng(bytes) {
|
|
@@ -367,6 +472,7 @@ function decodePng(bytes) {
|
|
|
367
472
|
let interlaceMethod = 0;
|
|
368
473
|
let palette;
|
|
369
474
|
let paletteAlpha;
|
|
475
|
+
let density = {};
|
|
370
476
|
const idatChunks = [];
|
|
371
477
|
let pos = 8;
|
|
372
478
|
while (pos + 12 <= bytes.length) {
|
|
@@ -381,7 +487,14 @@ function decodePng(bytes) {
|
|
|
381
487
|
interlaceMethod = bytes[dataOff + 12];
|
|
382
488
|
} else if (type === "PLTE") palette = bytes.subarray(dataOff, dataOff + len);
|
|
383
489
|
else if (type === "tRNS") paletteAlpha = bytes.subarray(dataOff, dataOff + len);
|
|
384
|
-
else if (type === "
|
|
490
|
+
else if (type === "pHYs") {
|
|
491
|
+
const perX = readU32BE(bytes, dataOff);
|
|
492
|
+
const perY = readU32BE(bytes, dataOff + 4);
|
|
493
|
+
if (bytes[dataOff + 8] === 1 && perX > 0 && perY > 0) density = {
|
|
494
|
+
dpiX: perX * .0254,
|
|
495
|
+
dpiY: perY * .0254
|
|
496
|
+
};
|
|
497
|
+
} else if (type === "IDAT") idatChunks.push(bytes.subarray(dataOff, dataOff + len));
|
|
385
498
|
else if (type === "IEND") break;
|
|
386
499
|
pos = dataOff + len + 4;
|
|
387
500
|
}
|
|
@@ -397,7 +510,10 @@ function decodePng(bytes) {
|
|
|
397
510
|
const channels = pngChannels(colorType);
|
|
398
511
|
if (channels === 0) throw new Error(`PNG color type ${colorType} not supported`);
|
|
399
512
|
const raw = decodeSamples(unzlibSync(concatBytes(idatChunks)), width, height, channels, bitDepth, interlaceMethod === 1, colorType !== 3);
|
|
400
|
-
return
|
|
513
|
+
return {
|
|
514
|
+
...splitChannels(width, height, colorType, raw, palette, paletteAlpha),
|
|
515
|
+
...density
|
|
516
|
+
};
|
|
401
517
|
}
|
|
402
518
|
/**
|
|
403
519
|
* §7.2 / §9 — the image's samples, one byte each, in `width × height ×
|
|
@@ -665,4 +781,4 @@ function concatBytes(parts) {
|
|
|
665
781
|
return out;
|
|
666
782
|
}
|
|
667
783
|
//#endregion
|
|
668
|
-
export { detectImageFormat, prepareImage };
|
|
784
|
+
export { detectImageFormat, knockOutColor, prepareImage };
|