opt-cc/api/tools/signature-tool.js

267 lines
9.8 KiB
JavaScript
Raw Normal View History

2018-03-12 09:26:44 +01:00
'use strict';
2017-05-10 11:04:06 +02:00
// modules used for graphic manipulation
const jimp = require('jimp');
const imagemin = require('imagemin');
const imageminpngquant = require('imagemin-pngquant');
const sha1 = require('node-sha1');
2017-05-10 11:04:06 +02:00
const async = require('async');
// Mongoose Model using mongoDB
const UserModel = require('../models/user');
const AwardingModel = require('../models/awarding');
const RankModel = require('../models/rank');
// http error code table
const codes = require('../routes/http-codes');
// standard input/output file extension
const fileExt = '.png';
const resourceDir = __dirname + '/../resource/';
let createSignature = (userId, res, next) => {
let loadedImage;
let user;
if (!String(userId).match(/^[0-9a-fA-F]{24}$/)) {
let error = new Error('no valid ID ' + userId);
error.status = codes.wrongdatatyperequest;
return next(error);
}
UserModel.findById(userId, ['username', 'rankLvl', 'squadId'])
2018-02-26 09:04:27 +01:00
.populate('squadId', ['name', 'fraction'])
.exec()
.then((resUser) => {
user = resUser;
let platePath;
if (resUser && resUser.squadId && resUser.squadId.fraction === 'BLUFOR') {
platePath = __dirname + '/backplate/blufor' + fileExt;
} else if (resUser && resUser.squadId && resUser.squadId.fraction === 'OPFOR') {
platePath = __dirname + '/backplate/opfor' + fileExt;
}
2017-05-10 11:04:06 +02:00
2018-02-26 09:04:27 +01:00
// kill process on undefined platePath
if (!platePath) {
throw new Error('Fraction not defined for user with id ' + userId);
}
2018-03-12 09:26:44 +01:00
return jimp.read(platePath);
2018-02-26 09:04:27 +01:00
})
.then((image) => {
loadedImage = image;
}).then(() => {
return jimp.loadFont(__dirname + '/font/DEVAJU_SANS_19.fnt');
})
.then((font) => {
2018-03-12 09:26:44 +01:00
loadedImage.print(font, 128, 8, user.username);
2018-02-26 09:04:27 +01:00
})
.then(() => {
return jimp.loadFont(__dirname + '/font/DEJAVU_SANS_13.fnt');
})
.then((font) => {
loadedImage.print(font, 128, 35, user.squadId.name);
return font;
})
.then((font) => {
let rankH;
let rankW;
let rankX;
let rankY;
2017-05-10 11:04:06 +02:00
2018-02-26 09:04:27 +01:00
RankModel.findOne({'level': user.rankLvl, 'fraction': user.squadId.fraction}, (err, result) => {
if (err) {
2018-03-12 09:26:44 +01:00
return next(err);
2018-02-26 09:04:27 +01:00
}
2017-05-10 11:04:06 +02:00
2018-02-26 09:04:27 +01:00
if (result) {
if (user.squadId.fraction === 'BLUFOR') {
rankW = 25;
rankH = 60;
rankX = 36;
rankY = 34;
} else {
rankW = 37;
rankH = 58;
rankX = 30;
rankY = 34;
}
2018-02-26 09:04:27 +01:00
jimp.read(resourceDir + 'rank/' + result._id + fileExt)
.then((rankImage) => {
rankImage.resize(rankW, rankH);
loadedImage
.print(font, 128, 55, result.name)
2018-03-12 09:26:44 +01:00
.composite(rankImage, rankX, rankY);
2018-02-26 09:04:27 +01:00
})
.then(() => {
addDecorationsAndSave(userId, loadedImage, res, next);
2018-03-12 09:26:44 +01:00
});
2018-02-26 09:04:27 +01:00
} else {
// user has not any assignable rank in his fraction at this point,
// e.g. assigned rank has been deleted or switched fraction so rankLvl is not defined
addDecorationsAndSave(userId, loadedImage, res, next);
}
2018-03-12 09:26:44 +01:00
});
2018-02-26 09:04:27 +01:00
})
.catch((err) => {
next(err);
2018-03-12 09:26:44 +01:00
});
2017-05-10 11:04:06 +02:00
};
/**
* query decorations according to user id and
* add all graphics into given image
*
* @param {number} userId
* @param {Object} loadedImage - background image
* @param {function} res
* @param {function} next
2017-05-10 11:04:06 +02:00
*/
let addDecorationsAndSave = (userId, loadedImage, res, next) => {
const medalW = 21;
const medalH = 40;
const ribbonW = 53;
const ribbonH = 15;
let medalPx = 630;
let medalPy = 5;
let ribbonPx = 598;
let ribbonPy = 95;
AwardingModel.find({
'userId': userId,
2018-03-12 09:26:44 +01:00
'confirmed': 1,
}, ['decorationId', 'date']).populate('decorationId', ['isMedal', 'fraction'])
2018-02-26 09:04:27 +01:00
.exec((err, awardings) => {
if (err) {
return next(err);
}
if (awardings.length > 0) {
2018-03-12 09:26:44 +01:00
// TODO: simplify this sorting hell
2018-02-26 09:04:27 +01:00
awardings.sort((a1, a2) => {
if (!a1.decorationId.isMedal && !a2.decorationId.isMedal) {
if (a1.decorationId.fraction === a2.decorationId.fraction) {
if (a1.date !== a2.date) {
if (a1.date < a2.date) {
return -1;
}
if (a1.date > a2.date) {
return 1;
}
}
return 0;
} else {
if (a1.decorationId.fraction === 'GLOBAL' && a2.decorationId.fraction !== 'GLOBAL') {
return -1;
}
if (a2.decorationId.fraction === 'GLOBAL' && a1.decorationId.fraction !== 'GLOBAL') {
return 1;
}
}
}
if (a1.decorationId.isMedal !== a2.decorationId.isMedal) {
if (a1.decorationId.isMedal && !a2.decorationId.isMedal) {
return 1;
}
if (!a1.decorationId.isMedal && a2.decorationId.isMedal) {
return -1;
}
}
if (a1.decorationId.isMedal && a2.decorationId.isMedal) {
if (a1.date !== a2.date) {
if (a1.date < a2.date) {
return -1;
}
if (a1.date > a2.date) {
return 1;
}
}
return 0;
}
});
2018-02-26 09:04:27 +01:00
// use synchronized call to keep correct order of decorations
async.eachSeries(awardings, (award, callback) => {
2018-03-12 09:59:43 +01:00
jimp.read(resourceDir + 'decoration/' + award.decorationId._id + fileExt)
.then((decorationImage) => {
if (award.decorationId.isMedal) {
decorationImage.resize(medalW, medalH);
loadedImage.composite(decorationImage, medalPx, medalPy);
if (medalPy === 5) {
medalPx = medalPx - 1 - medalW;
} else {
medalPx = medalPx + 1 + medalW;
}
if (medalPx <= 300) {
medalPy = medalPy + 3 + medalH;
}
} else {
decorationImage.resize(ribbonW, ribbonH);
loadedImage.composite(decorationImage, ribbonPx, ribbonPy);
ribbonPx = ribbonPx - 2 - ribbonW;
if (ribbonPx <= 154) {
ribbonPy = ribbonPy - 3 - ribbonH;
ribbonPx = 598;
}
}
callback();
});
2018-02-26 09:04:27 +01:00
}, (err) => {
if (err) {
throw err;
}
compareImagesAndSave(loadedImage, userId, res, next);
});
} else {
compareImagesAndSave(loadedImage, userId, res, next);
}
}
2018-03-12 09:26:44 +01:00
);
2017-05-10 11:04:06 +02:00
};
let compareImagesAndSave = (generatedImage, userId, res, next) => {
return jimp.read(resourceDir + 'signature/big/' + userId + fileExt)
2018-02-26 09:04:27 +01:00
.then((oldImage) => {
// compare hashes of image map to recognize difference
const sig1 = sha1(generatedImage.bitmap.data);
const sig2 = sha1(oldImage.bitmap.data);
2018-02-26 09:04:27 +01:00
if (sig1 !== sig2) {
2018-03-12 09:26:44 +01:00
saveJimpImageAndCompress(generatedImage, userId, res, next);
2018-02-26 09:04:27 +01:00
} else {
res.locals.items = {status: 'nothing to do'};
next();
}
})
.catch((err) => {
saveJimpImageAndCompress(generatedImage, userId, res, next);
2018-03-12 09:26:44 +01:00
});
2017-05-10 11:04:06 +02:00
};
/**
* Write Jimp image to file system
* Gets minified after 3 seconds timeout
*
* @param {Object} image - Jimp image
* @param {number} userId - user id of signature owner
* @param {function} res - express response object
* @param {function} next - express next-function
2017-05-10 11:04:06 +02:00
*/
let saveJimpImageAndCompress = (image, userId, res, next) => {
image.write(resourceDir + 'signature/big/' + userId + fileExt);
setTimeout(() => {
imagemin([resourceDir + 'signature/big/' + userId + fileExt], resourceDir + 'signature/', {
plugins: [
2018-03-12 09:26:44 +01:00
imageminpngquant({quality: '65-80'}),
],
2017-05-10 11:04:06 +02:00
}).then((files) => {
res.locals.items = {status: 'success'};
return next();
2017-06-10 22:07:32 +02:00
}).catch((error) => {
2018-03-12 09:26:44 +01:00
console.log(error);
});
2017-05-10 11:04:06 +02:00
}, 3000);
};
module.exports = createSignature;