1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
// @ts-ignore
import { red } from 'kleur/colors';
import { error } from '../utils/logger.js';
import { metadata } from '../utils/metadata.js';
import { isRemoteImage } from '../utils/paths.js';
import { processBuffer } from '../vendor/squoosh/image-pool.js';
import type { Operation } from '../vendor/squoosh/image.js';
import type { OutputFormat, TransformOptions } from './index.js';
import { BaseSSRService } from './index.js';
class SquooshService extends BaseSSRService {
async processAvif(image: any, transform: TransformOptions) {
const encodeOptions = transform.quality
? { avif: { quality: transform.quality } }
: { avif: {} };
await image.encode(encodeOptions);
const data = await image.encodedWith.avif;
return {
data: data.binary,
format: 'avif' as OutputFormat,
};
}
async processJpeg(image: any, transform: TransformOptions) {
const encodeOptions = transform.quality
? { mozjpeg: { quality: transform.quality } }
: { mozjpeg: {} };
await image.encode(encodeOptions);
const data = await image.encodedWith.mozjpeg;
return {
data: data.binary,
format: 'jpeg' as OutputFormat,
};
}
async processPng(image: any, transform: TransformOptions) {
await image.encode({ oxipng: {} });
const data = await image.encodedWith.oxipng;
return {
data: data.binary,
format: 'png' as OutputFormat,
};
}
async processWebp(image: any, transform: TransformOptions) {
const encodeOptions = transform.quality
? { webp: { quality: transform.quality } }
: { webp: {} };
await image.encode(encodeOptions);
const data = await image.encodedWith.webp;
return {
data: data.binary,
format: 'webp' as OutputFormat,
};
}
async autorotate(
transform: TransformOptions,
inputBuffer: Buffer
): Promise<Operation | undefined> {
// check EXIF orientation data and rotate the image if needed
try {
const meta = await metadata(transform.src, inputBuffer);
switch (meta?.orientation) {
case 3:
case 4:
return { type: 'rotate', numRotations: 2 };
case 5:
case 6:
return { type: 'rotate', numRotations: 1 };
case 7:
case 8:
return { type: 'rotate', numRotations: 3 };
}
} catch {}
}
async transform(inputBuffer: Buffer, transform: TransformOptions) {
const operations: Operation[] = [];
if (!isRemoteImage(transform.src)) {
const autorotate = await this.autorotate(transform, inputBuffer);
if (autorotate) {
operations.push(autorotate);
}
}
if (transform.width || transform.height) {
const width = transform.width && Math.round(transform.width);
const height = transform.height && Math.round(transform.height);
operations.push({
type: 'resize',
width,
height,
});
}
if (!transform.format) {
error({
level: 'info',
prefix: false,
message: red(`Unknown image output: "${transform.format}" used for ${transform.src}`),
});
throw new Error(`Unknown image output: "${transform.format}" used for ${transform.src}`);
}
const data = await processBuffer(
inputBuffer,
operations,
transform.format,
transform.quality
);
return {
data: Buffer.from(data),
format: transform.format,
};
}
}
const service = new SquooshService();
export default service;
|