@paroicms/server-image-cache-engine
Version:
The image variant engine that we use at Paroi.
93 lines • 3.68 kB
JavaScript
import sharp from "sharp";
import { authorizedMediaTypes } from "./constants.js";
import { sharpQueue } from "./image-processor-setup.js";
import { convertSharpFormatToMediaType, getQuality } from "./internal/resizer.js";
export async function processOriginalImage({ binaryFile, mediaType, ApiError, policy, defaultPolicy, qualityPolicy, }) {
const weightB = binaryFile.byteLength;
const sharpInst = sharp(binaryFile);
const { width, height, format } = await sharpQueue(() => sharpInst.metadata());
if (width === undefined || height === undefined || !format) {
throw new Error("cannot get image metadata");
}
const foundMediaType = convertSharpFormatToMediaType(format);
if (foundMediaType !== mediaType) {
throw new ApiError(`mismatch media type '${mediaType}' with '${foundMediaType}'`, 400);
}
if (!authorizedMediaTypes.has(mediaType)) {
throw new ApiError("Invalid image type", 400);
}
const weightLimitB = policy?.weightLimitB ?? defaultPolicy.weightLimitB;
const areaLimitPx = policy?.areaLimitPx ?? defaultPolicy.areaLimitPx;
if (mediaType === "image/gif" || mediaType === "image/svg+xml") {
if (weightLimitB && weightB > weightLimitB) {
throw new ApiError("Image weight exceeds limit", 400);
}
return {
binaryFile,
rawWidth: width,
rawHeight: height,
mediaType,
weightB,
lossless: undefined,
};
}
const input = {
rawWidth: width,
rawHeight: height,
mediaType,
sharpInst,
};
if (mediaType === "image/webp") {
if (weightLimitB === undefined || weightB <= weightLimitB) {
return {
binaryFile,
rawWidth: width,
rawHeight: height,
mediaType,
weightB,
lossless: undefined,
};
}
return await convertToWebp({ input, qualityPolicy, areaLimitPx, lossless: false });
}
if (mediaType === "image/png") {
return await convertToWebp({
input,
qualityPolicy,
areaLimitPx,
// WebP lossless images are 26% smaller in size compared to PNGs
// See: https://developers.google.com/speed/webp
lossless: weightLimitB === undefined || weightB <= weightLimitB * 1.35,
});
}
return await convertToWebp({ input, qualityPolicy, areaLimitPx, lossless: false });
}
async function convertToWebp({ input, qualityPolicy, areaLimitPx, lossless, }) {
const { rawWidth, rawHeight } = areaLimitPx === undefined ? input : computeOriginalSize(input, areaLimitPx);
const quality = getQuality({ rawWidth, rawHeight }, qualityPolicy);
const { data, info } = await sharpQueue(() => input.sharpInst
.resize({ width: rawWidth, height: rawHeight })
.webp({
lossless,
quality,
})
.toBuffer({ resolveWithObject: true }));
return {
rawWidth: info.width,
rawHeight: info.height,
weightB: data.byteLength,
mediaType: "image/webp",
lossless,
binaryFile: data,
};
}
function computeOriginalSize(input, areaLimitPx) {
const inputArea = input.rawWidth * input.rawHeight;
if (inputArea <= areaLimitPx)
return input;
const ratio = input.rawWidth / input.rawHeight;
const width = Math.trunc(Math.sqrt(areaLimitPx * ratio));
const height = Math.round((input.rawHeight / input.rawWidth) * width);
return { rawWidth: width, rawHeight: height };
}
//# sourceMappingURL=original-images-processing.js.map