diff --git a/src/core/lib/FileType.mjs b/src/core/lib/FileType.mjs index d38ddc80..ef6cfb03 100644 --- a/src/core/lib/FileType.mjs +++ b/src/core/lib/FileType.mjs @@ -703,6 +703,39 @@ export function detectFileType(buf) { } +/** + * Detects whether the given buffer is a file of the type specified. + * + * @param {string|RegExp} type + * @param {Uint8Array} buf + * @returns {string|false} The mime type or false if the type does not match + */ +export function isType(type, buf) { + const types = detectFileType(buf); + + if (!(types && types.length)) return false; + + if (typeof type === "string") { + return types[0].mime.startsWith(type) ? types[0].mime : false; + } else if (type instanceof RegExp) { + return type.test(types[0].mime) ? types[0].mime : false; + } else { + throw new Error("Invalid type input."); + } +} + + +/** + * Detects whether the given buffer contains an image file. + * + * @param {Uint8Array} buf + * @returns {string|false} The mime type or false if the type does not match + */ +export function isImage(buf) { + return isType("image", buf); +} + + /** * Attempts to extract a file from a data stream given its offset and extractor function. * diff --git a/src/core/operations/GenerateQRCode.mjs b/src/core/operations/GenerateQRCode.mjs index edab6d40..ac7e5c5c 100644 --- a/src/core/operations/GenerateQRCode.mjs +++ b/src/core/operations/GenerateQRCode.mjs @@ -8,7 +8,7 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; import qr from "qr-image"; import { toBase64 } from "../lib/Base64"; -import Magic from "../lib/Magic"; +import { isImage } from "../lib/FileType"; import Utils from "../Utils"; /** @@ -100,9 +100,9 @@ class GenerateQRCode extends Operation { if (format === "PNG") { let dataURI = "data:"; - const type = Magic.magicFileType(data); - if (type && type.mime.indexOf("image") === 0){ - dataURI += type.mime + ";"; + const mime = isImage(data); + if (mime){ + dataURI += mime + ";"; } else { throw new OperationError("Invalid PNG file generated by QR image"); } diff --git a/src/core/operations/ParseQRCode.mjs b/src/core/operations/ParseQRCode.mjs index 75a24d55..ef7af6d7 100644 --- a/src/core/operations/ParseQRCode.mjs +++ b/src/core/operations/ParseQRCode.mjs @@ -6,7 +6,7 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; -import Magic from "../lib/Magic"; +import { isImage } from "../lib/FileType"; import jsqr from "jsqr"; import jimp from "jimp"; @@ -42,64 +42,61 @@ class ParseQRCode extends Operation { * @returns {string} */ async run(input, args) { - const type = Magic.magicFileType(input); const [normalise] = args; // Make sure that the input is an image - if (type && type.mime.indexOf("image") === 0) { - let image = input; + if (!isImage(input)) throw new OperationError("Invalid file type."); - if (normalise) { - // Process the image to be easier to read by jsqr - // Disables the alpha channel - // Sets the image default background to white - // Normalises the image colours - // Makes the image greyscale - // Converts image to a JPEG - image = await new Promise((resolve, reject) => { - jimp.read(Buffer.from(input)) - .then(image => { - image - .rgba(false) - .background(0xFFFFFFFF) - .normalize() - .greyscale() - .getBuffer(jimp.MIME_JPEG, (error, result) => { - resolve(result); - }); - }) - .catch(err => { - reject(new OperationError("Error reading the image file.")); - }); - }); - } + let image = input; - if (image instanceof OperationError) { - throw image; - } - - return new Promise((resolve, reject) => { - jimp.read(Buffer.from(image)) + if (normalise) { + // Process the image to be easier to read by jsqr + // Disables the alpha channel + // Sets the image default background to white + // Normalises the image colours + // Makes the image greyscale + // Converts image to a JPEG + image = await new Promise((resolve, reject) => { + jimp.read(Buffer.from(input)) .then(image => { - if (image.bitmap != null) { - const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight()); - if (qrData != null) { - resolve(qrData.data); - } else { - reject(new OperationError("Couldn't read a QR code from the image.")); - } - } else { - reject(new OperationError("Error reading the image file.")); - } + image + .rgba(false) + .background(0xFFFFFFFF) + .normalize() + .greyscale() + .getBuffer(jimp.MIME_JPEG, (error, result) => { + resolve(result); + }); }) .catch(err => { reject(new OperationError("Error reading the image file.")); }); }); - } else { - throw new OperationError("Invalid file type."); } + if (image instanceof OperationError) { + throw image; + } + + return new Promise((resolve, reject) => { + jimp.read(Buffer.from(image)) + .then(image => { + if (image.bitmap != null) { + const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight()); + if (qrData != null) { + resolve(qrData.data); + } else { + reject(new OperationError("Couldn't read a QR code from the image.")); + } + } else { + reject(new OperationError("Error reading the image file.")); + } + }) + .catch(err => { + reject(new OperationError("Error reading the image file.")); + }); + }); + } } diff --git a/src/core/operations/PlayMedia.mjs b/src/core/operations/PlayMedia.mjs index d0ec78cc..98b7d088 100644 --- a/src/core/operations/PlayMedia.mjs +++ b/src/core/operations/PlayMedia.mjs @@ -9,7 +9,7 @@ import { fromHex } from "../lib/Hex"; import Operation from "../Operation"; import OperationError from "../errors/OperationError"; import Utils from "../Utils"; -import { detectFileType } from "../lib/FileType"; +import { isType, detectFileType } from "../lib/FileType"; /** * PlayMedia operation @@ -66,8 +66,7 @@ class PlayMedia extends Operation { // Determine file type - const types = detectFileType(input); - if (!(types && types.length && /^audio|video/.test(types[0].mime))) { + if (!isType(/^(audio|video)/, input)) { throw new OperationError("Invalid or unrecognised file type"); } diff --git a/src/core/operations/RenderImage.mjs b/src/core/operations/RenderImage.mjs index 4554c96b..07866eaf 100644 --- a/src/core/operations/RenderImage.mjs +++ b/src/core/operations/RenderImage.mjs @@ -9,7 +9,7 @@ import { fromHex } from "../lib/Hex"; import Operation from "../Operation"; import OperationError from "../errors/OperationError"; import Utils from "../Utils"; -import {detectFileType} from "../lib/FileType"; +import {isImage} from "../lib/FileType"; /** * Render Image operation @@ -72,8 +72,7 @@ class RenderImage extends Operation { } // Determine file type - const types = detectFileType(input); - if (!(types.length && types[0].mime.indexOf("image") === 0)) { + if (!isImage(input)) { throw new OperationError("Invalid file type"); } @@ -92,9 +91,9 @@ class RenderImage extends Operation { let dataURI = "data:"; // Determine file type - const types = detectFileType(data); - if (types.length && types[0].mime.indexOf("image") === 0) { - dataURI += types[0].mime + ";"; + const mime = isImage(data); + if (mime) { + dataURI += mime + ";"; } else { throw new OperationError("Invalid file type"); } diff --git a/src/core/operations/SplitColourChannels.mjs b/src/core/operations/SplitColourChannels.mjs index f11ca14e..c38af409 100644 --- a/src/core/operations/SplitColourChannels.mjs +++ b/src/core/operations/SplitColourChannels.mjs @@ -7,7 +7,7 @@ import Operation from "../Operation"; import OperationError from "../errors/OperationError"; import Utils from "../Utils"; -import Magic from "../lib/Magic"; +import {isImage} from "../lib/FileType"; import jimp from "jimp"; @@ -38,56 +38,53 @@ class SplitColourChannels extends Operation { * @returns {List} */ async run(input, args) { - const type = Magic.magicFileType(input); // Make sure that the input is an image - if (type && type.mime.indexOf("image") === 0) { - const parsedImage = await jimp.read(Buffer.from(input)); + if (!isImage(input)) throw new OperationError("Invalid file type."); - const red = new Promise(async (resolve, reject) => { - try { - const split = parsedImage - .clone() - .color([ - {apply: "blue", params: [-255]}, - {apply: "green", params: [-255]} - ]) - .getBufferAsync(jimp.MIME_PNG); - resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"})); - } catch (err) { - reject(new OperationError(`Could not split red channel: ${err}`)); - } - }); + const parsedImage = await jimp.read(Buffer.from(input)); - const green = new Promise(async (resolve, reject) => { - try { - const split = parsedImage.clone() - .color([ - {apply: "red", params: [-255]}, - {apply: "blue", params: [-255]}, - ]).getBufferAsync(jimp.MIME_PNG); - resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"})); - } catch (err) { - reject(new OperationError(`Could not split green channel: ${err}`)); - } - }); + const red = new Promise(async (resolve, reject) => { + try { + const split = parsedImage + .clone() + .color([ + {apply: "blue", params: [-255]}, + {apply: "green", params: [-255]} + ]) + .getBufferAsync(jimp.MIME_PNG); + resolve(new File([new Uint8Array((await split).values())], "red.png", {type: "image/png"})); + } catch (err) { + reject(new OperationError(`Could not split red channel: ${err}`)); + } + }); - const blue = new Promise(async (resolve, reject) => { - try { - const split = parsedImage - .color([ - {apply: "red", params: [-255]}, - {apply: "green", params: [-255]}, - ]).getBufferAsync(jimp.MIME_PNG); - resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"})); - } catch (err) { - reject(new OperationError(`Could not split blue channel: ${err}`)); - } - }); + const green = new Promise(async (resolve, reject) => { + try { + const split = parsedImage.clone() + .color([ + {apply: "red", params: [-255]}, + {apply: "blue", params: [-255]}, + ]).getBufferAsync(jimp.MIME_PNG); + resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"})); + } catch (err) { + reject(new OperationError(`Could not split green channel: ${err}`)); + } + }); - return await Promise.all([red, green, blue]); - } else { - throw new OperationError("Invalid file type."); - } + const blue = new Promise(async (resolve, reject) => { + try { + const split = parsedImage + .color([ + {apply: "red", params: [-255]}, + {apply: "green", params: [-255]}, + ]).getBufferAsync(jimp.MIME_PNG); + resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"})); + } catch (err) { + reject(new OperationError(`Could not split blue channel: ${err}`)); + } + }); + + return await Promise.all([red, green, blue]); } /**