Added 'isImage' and 'isType' functions

This commit is contained in:
n1474335 2018-12-26 18:40:27 +00:00
parent f355fe3447
commit f4f9b5c91c
6 changed files with 130 additions and 105 deletions

View File

@ -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. * Attempts to extract a file from a data stream given its offset and extractor function.
* *

View File

@ -8,7 +8,7 @@ import Operation from "../Operation";
import OperationError from "../errors/OperationError"; import OperationError from "../errors/OperationError";
import qr from "qr-image"; import qr from "qr-image";
import { toBase64 } from "../lib/Base64"; import { toBase64 } from "../lib/Base64";
import Magic from "../lib/Magic"; import { isImage } from "../lib/FileType";
import Utils from "../Utils"; import Utils from "../Utils";
/** /**
@ -100,9 +100,9 @@ class GenerateQRCode extends Operation {
if (format === "PNG") { if (format === "PNG") {
let dataURI = "data:"; let dataURI = "data:";
const type = Magic.magicFileType(data); const mime = isImage(data);
if (type && type.mime.indexOf("image") === 0){ if (mime){
dataURI += type.mime + ";"; dataURI += mime + ";";
} else { } else {
throw new OperationError("Invalid PNG file generated by QR image"); throw new OperationError("Invalid PNG file generated by QR image");
} }

View File

@ -6,7 +6,7 @@
import Operation from "../Operation"; import Operation from "../Operation";
import OperationError from "../errors/OperationError"; import OperationError from "../errors/OperationError";
import Magic from "../lib/Magic"; import { isImage } from "../lib/FileType";
import jsqr from "jsqr"; import jsqr from "jsqr";
import jimp from "jimp"; import jimp from "jimp";
@ -42,64 +42,61 @@ class ParseQRCode extends Operation {
* @returns {string} * @returns {string}
*/ */
async run(input, args) { async run(input, args) {
const type = Magic.magicFileType(input);
const [normalise] = args; const [normalise] = args;
// Make sure that the input is an image // Make sure that the input is an image
if (type && type.mime.indexOf("image") === 0) { if (!isImage(input)) throw new OperationError("Invalid file type.");
let image = input;
if (normalise) { let image = input;
// 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."));
});
});
}
if (image instanceof OperationError) { if (normalise) {
throw image; // Process the image to be easier to read by jsqr
} // Disables the alpha channel
// Sets the image default background to white
return new Promise((resolve, reject) => { // Normalises the image colours
jimp.read(Buffer.from(image)) // Makes the image greyscale
// Converts image to a JPEG
image = await new Promise((resolve, reject) => {
jimp.read(Buffer.from(input))
.then(image => { .then(image => {
if (image.bitmap != null) { image
const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight()); .rgba(false)
if (qrData != null) { .background(0xFFFFFFFF)
resolve(qrData.data); .normalize()
} else { .greyscale()
reject(new OperationError("Couldn't read a QR code from the image.")); .getBuffer(jimp.MIME_JPEG, (error, result) => {
} resolve(result);
} else { });
reject(new OperationError("Error reading the image file."));
}
}) })
.catch(err => { .catch(err => {
reject(new OperationError("Error reading the image file.")); 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."));
});
});
} }
} }

View File

@ -9,7 +9,7 @@ import { fromHex } from "../lib/Hex";
import Operation from "../Operation"; import Operation from "../Operation";
import OperationError from "../errors/OperationError"; import OperationError from "../errors/OperationError";
import Utils from "../Utils"; import Utils from "../Utils";
import { detectFileType } from "../lib/FileType"; import { isType, detectFileType } from "../lib/FileType";
/** /**
* PlayMedia operation * PlayMedia operation
@ -66,8 +66,7 @@ class PlayMedia extends Operation {
// Determine file type // Determine file type
const types = detectFileType(input); if (!isType(/^(audio|video)/, input)) {
if (!(types && types.length && /^audio|video/.test(types[0].mime))) {
throw new OperationError("Invalid or unrecognised file type"); throw new OperationError("Invalid or unrecognised file type");
} }

View File

@ -9,7 +9,7 @@ import { fromHex } from "../lib/Hex";
import Operation from "../Operation"; import Operation from "../Operation";
import OperationError from "../errors/OperationError"; import OperationError from "../errors/OperationError";
import Utils from "../Utils"; import Utils from "../Utils";
import {detectFileType} from "../lib/FileType"; import {isImage} from "../lib/FileType";
/** /**
* Render Image operation * Render Image operation
@ -72,8 +72,7 @@ class RenderImage extends Operation {
} }
// Determine file type // Determine file type
const types = detectFileType(input); if (!isImage(input)) {
if (!(types.length && types[0].mime.indexOf("image") === 0)) {
throw new OperationError("Invalid file type"); throw new OperationError("Invalid file type");
} }
@ -92,9 +91,9 @@ class RenderImage extends Operation {
let dataURI = "data:"; let dataURI = "data:";
// Determine file type // Determine file type
const types = detectFileType(data); const mime = isImage(data);
if (types.length && types[0].mime.indexOf("image") === 0) { if (mime) {
dataURI += types[0].mime + ";"; dataURI += mime + ";";
} else { } else {
throw new OperationError("Invalid file type"); throw new OperationError("Invalid file type");
} }

View File

@ -7,7 +7,7 @@
import Operation from "../Operation"; import Operation from "../Operation";
import OperationError from "../errors/OperationError"; import OperationError from "../errors/OperationError";
import Utils from "../Utils"; import Utils from "../Utils";
import Magic from "../lib/Magic"; import {isImage} from "../lib/FileType";
import jimp from "jimp"; import jimp from "jimp";
@ -38,56 +38,53 @@ class SplitColourChannels extends Operation {
* @returns {List<File>} * @returns {List<File>}
*/ */
async run(input, args) { async run(input, args) {
const type = Magic.magicFileType(input);
// Make sure that the input is an image // Make sure that the input is an image
if (type && type.mime.indexOf("image") === 0) { if (!isImage(input)) throw new OperationError("Invalid file type.");
const parsedImage = await jimp.read(Buffer.from(input));
const red = new Promise(async (resolve, reject) => { const parsedImage = await jimp.read(Buffer.from(input));
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 green = new Promise(async (resolve, reject) => { const red = new Promise(async (resolve, reject) => {
try { try {
const split = parsedImage.clone() const split = parsedImage
.color([ .clone()
{apply: "red", params: [-255]}, .color([
{apply: "blue", params: [-255]}, {apply: "blue", params: [-255]},
]).getBufferAsync(jimp.MIME_PNG); {apply: "green", params: [-255]}
resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"})); ])
} catch (err) { .getBufferAsync(jimp.MIME_PNG);
reject(new OperationError(`Could not split green channel: ${err}`)); 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) => { const green = new Promise(async (resolve, reject) => {
try { try {
const split = parsedImage const split = parsedImage.clone()
.color([ .color([
{apply: "red", params: [-255]}, {apply: "red", params: [-255]},
{apply: "green", params: [-255]}, {apply: "blue", params: [-255]},
]).getBufferAsync(jimp.MIME_PNG); ]).getBufferAsync(jimp.MIME_PNG);
resolve(new File([new Uint8Array((await split).values())], "blue.png", {type: "image/png"})); resolve(new File([new Uint8Array((await split).values())], "green.png", {type: "image/png"}));
} catch (err) { } catch (err) {
reject(new OperationError(`Could not split blue channel: ${err}`)); reject(new OperationError(`Could not split green channel: ${err}`));
} }
}); });
return await Promise.all([red, green, blue]); const blue = new Promise(async (resolve, reject) => {
} else { try {
throw new OperationError("Invalid file type."); 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]);
} }
/** /**