Profile: Composite profile using imagemagick
by spawning an external process. Very slow but it works.
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
<svg width="100%" height="100%" viewBox="0 0 600 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
<svg width="100%" height="100%" viewBox="0 0 1200 600" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
<path id="BG" d="M600,6.097C600,2.732 597.268,0 593.903,0L6.097,0C2.732,0 0,2.732 0,6.097L0,293.903C0,297.268 2.732,300 6.097,300L593.903,300C597.268,300 600,297.268 600,293.903L600,6.097ZM13.189,42.808L163.189,42.808L163.189,292.808L13.189,292.808L13.189,42.808ZM267.866,150L352.866,150L352.866,291.667L267.866,291.667L267.866,150ZM175,150L260,150L260,291.667L175,291.667L175,150ZM360.366,150L445.366,150L445.366,291.667L360.366,291.667L360.366,150Z" style="fill:rgb(61,61,61);"/>
|
<g id="BG" transform="matrix(2,0,0,2,-1.23161e-13,0)">
|
||||||
<g id="BADGES" transform="matrix(0.511675,0,0,0.501053,-25.2132,12.3596)">
|
<path d="M600,6.097C600,2.732 597.268,0 593.903,0L6.097,0C2.732,0 0,2.732 0,6.097L0,293.903C0,297.268 2.732,300 6.097,300L593.903,300C597.268,300 600,297.268 600,293.903L600,6.097ZM13.189,42.808L163.189,42.808L163.189,292.808L13.189,292.808L13.189,42.808ZM267.866,150L352.866,150L352.866,291.667L267.866,291.667L267.866,150ZM175,150L260,150L260,291.667L175,291.667L175,150ZM360.366,150L445.366,150L445.366,291.667L360.366,291.667L360.366,150Z" style="fill:rgb(61,61,61);"/>
|
||||||
|
</g>
|
||||||
|
<g id="BADGES" transform="matrix(1.02335,0,0,1.00211,-50.4264,24.7193)">
|
||||||
<path d="M1180.85,291.997C1180.85,282.452 1173.27,274.702 1163.92,274.702L953.493,274.702C944.146,274.702 936.558,282.452 936.558,291.997L936.558,540.146C936.558,549.691 944.146,557.44 953.493,557.44L1163.92,557.44C1173.27,557.44 1180.85,549.691 1180.85,540.146L1180.85,291.997Z" style="fill:rgb(235,235,235);"/>
|
<path d="M1180.85,291.997C1180.85,282.452 1173.27,274.702 1163.92,274.702L953.493,274.702C944.146,274.702 936.558,282.452 936.558,291.997L936.558,540.146C936.558,549.691 944.146,557.44 953.493,557.44L1163.92,557.44C1173.27,557.44 1180.85,549.691 1180.85,540.146L1180.85,291.997Z" style="fill:rgb(235,235,235);"/>
|
||||||
<clipPath id="_clip1">
|
<clipPath id="_clip1">
|
||||||
<path d="M1180.85,291.997C1180.85,282.452 1173.27,274.702 1163.92,274.702L953.493,274.702C944.146,274.702 936.558,282.452 936.558,291.997L936.558,540.146C936.558,549.691 944.146,557.44 953.493,557.44L1163.92,557.44C1173.27,557.44 1180.85,549.691 1180.85,540.146L1180.85,291.997Z"/>
|
<path d="M1180.85,291.997C1180.85,282.452 1173.27,274.702 1163.92,274.702L953.493,274.702C944.146,274.702 936.558,282.452 936.558,291.997L936.558,540.146C936.558,549.691 944.146,557.44 953.493,557.44L1163.92,557.44C1173.27,557.44 1180.85,549.691 1180.85,540.146L1180.85,291.997Z"/>
|
||||||
@@ -25,11 +27,11 @@
|
|||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g id="PROFILE" transform="matrix(0.5,0,0,0.5,5.68434e-14,0)">
|
<g id="PROFILE">
|
||||||
<g id="BODY" transform="matrix(0.994765,0,0,1.386,-12.9709,-59.6438)">
|
<g id="BODY" transform="matrix(0.994765,0,0,1.386,-12.9709,-59.6438)">
|
||||||
<path d="M1177.13,71.893C1177.13,59.947 1163.62,50.248 1146.98,50.248L395.039,50.248C378.394,50.248 364.881,59.947 364.881,71.893L364.881,227.016C364.881,238.962 378.394,248.661 395.039,248.661L1146.98,248.661C1163.62,248.661 1177.13,238.962 1177.13,227.016L1177.13,71.893Z" style="fill:rgb(227,90,255);"/>
|
<path d="M1177.13,61.071C1177.13,55.097 1170.38,50.248 1162.05,50.248L379.96,50.248C371.638,50.248 364.881,55.097 364.881,61.071L364.881,237.838C364.881,243.811 371.638,248.661 379.96,248.661L1162.05,248.661C1170.38,248.661 1177.13,243.811 1177.13,237.838L1177.13,61.071Z" style="fill:rgb(227,90,255);"/>
|
||||||
<clipPath id="_clip2">
|
<clipPath id="_clip2">
|
||||||
<path d="M1177.13,71.893C1177.13,59.947 1163.62,50.248 1146.98,50.248L395.039,50.248C378.394,50.248 364.881,59.947 364.881,71.893L364.881,227.016C364.881,238.962 378.394,248.661 395.039,248.661L1146.98,248.661C1163.62,248.661 1177.13,238.962 1177.13,227.016L1177.13,71.893Z"/>
|
<path d="M1177.13,61.071C1177.13,55.097 1170.38,50.248 1162.05,50.248L379.96,50.248C371.638,50.248 364.881,55.097 364.881,61.071L364.881,237.838C364.881,243.811 371.638,248.661 379.96,248.661L1162.05,248.661C1170.38,248.661 1177.13,243.811 1177.13,237.838L1177.13,61.071Z"/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<g clip-path="url(#_clip2)">
|
<g clip-path="url(#_clip2)">
|
||||||
<g transform="matrix(1.25332,0,0,0.899538,-99.307,18.6818)">
|
<g transform="matrix(1.25332,0,0,0.899538,-99.307,18.6818)">
|
||||||
@@ -45,9 +47,9 @@
|
|||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g id="HEADER" transform="matrix(0.994765,0,0,0.3276,-12.9709,-6.46125)">
|
<g id="HEADER" transform="matrix(0.994765,0,0,0.3276,-12.9709,-6.46125)">
|
||||||
<path d="M1177.13,141.823C1177.13,91.281 1163.62,50.248 1146.98,50.248L395.039,50.248C378.394,50.248 364.881,91.281 364.881,141.823L364.881,248.661L1177.13,248.661L1177.13,141.823Z" style="fill:rgb(255,79,79);"/>
|
<path d="M1177.13,96.036C1177.13,70.765 1170.38,50.248 1162.05,50.248L379.96,50.248C371.638,50.248 364.881,70.765 364.881,96.036L364.881,248.661L1177.13,248.661L1177.13,96.036Z" style="fill:rgb({{HEADER_COLOR}});"/>
|
||||||
<clipPath id="_clip3">
|
<clipPath id="_clip3">
|
||||||
<path d="M1177.13,141.823C1177.13,91.281 1163.62,50.248 1146.98,50.248L395.039,50.248C378.394,50.248 364.881,91.281 364.881,141.823L364.881,248.661L1177.13,248.661L1177.13,141.823Z"/>
|
<path d="M1177.13,96.036C1177.13,70.765 1170.38,50.248 1162.05,50.248L379.96,50.248C371.638,50.248 364.881,70.765 364.881,96.036L364.881,248.661L1177.13,248.661L1177.13,96.036Z"/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<g clip-path="url(#_clip3)">
|
<g clip-path="url(#_clip3)">
|
||||||
<g transform="matrix(1.87138,0,0,5.68249,-1010.59,-70.3181)">
|
<g transform="matrix(1.87138,0,0,5.68249,-1010.59,-70.3181)">
|
||||||
@@ -65,7 +67,7 @@
|
|||||||
<circle cx="399.913" cy="77.84" r="58.594" style="fill:rgb(79,255,171);"/>
|
<circle cx="399.913" cy="77.84" r="58.594" style="fill:rgb(79,255,171);"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g transform="matrix(1.42884,0,0,1.42884,-37.6688,-779.829)">
|
<g transform="matrix(2.85767,0,0,2.85767,-75.3376,-1559.66)">
|
||||||
<text x="44.839px" y="563.641px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">{{USERNAME}}</text>
|
<text x="44.839px" y="563.641px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:12px;">{{USERNAME}}</text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
@@ -1,6 +1,7 @@
|
|||||||
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
||||||
const { Card, User, Character } = require("../models");
|
const { Card, User, Character } = require("../models");
|
||||||
const UserUtils = require("../util/users");
|
const { UserUtils, Compositing, Rendering } = require("../util");
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
const pageSize = 8;
|
const pageSize = 8;
|
||||||
|
|
||||||
@@ -10,9 +11,26 @@ module.exports = {
|
|||||||
.setName("profile")
|
.setName("profile")
|
||||||
.setDescription("View your profile"),
|
.setDescription("View your profile"),
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply();
|
||||||
let user = await UserUtils.getUserByDiscordId(interaction.member.id);
|
let user = await UserUtils.getUserByDiscordId(interaction.member.id);
|
||||||
|
|
||||||
let profile = await user.getProfile();
|
let profile = await user.getProfile();
|
||||||
await interaction.reply(`json: ${JSON.stringify(profile)}`);
|
|
||||||
|
let profileTemplate = fs.readFileSync('/app/assets/profile/profile.svg').toString();
|
||||||
|
profileTemplate = profileTemplate.replace(/{{USERNAME}}/g, interaction.member.user.username);
|
||||||
|
profileTemplate = profileTemplate.replace(/{{HEADER_COLOR}}/g, '190,31,97');
|
||||||
|
|
||||||
|
let slots = ['slotOne', 'slotTwo', 'slotThree', 'slotFour'];
|
||||||
|
let renderedCards = [];
|
||||||
|
for (slot of slots) {
|
||||||
|
let card = await Card.findOne({ where: { id: profile[slot] }});
|
||||||
|
if (card) {
|
||||||
|
let cardImage = await Rendering.renderCard(card);
|
||||||
|
renderedCards.push(cardImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let profileImage = await Compositing.renderProfile(profile, profileTemplate, renderedCards);
|
||||||
|
await interaction.editReply({ files: [profileImage] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
42
util/compositing.js
Normal file
42
util/compositing.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const { spawn } = require('child_process');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const fs = require('fs');
|
||||||
|
const { Card } = require('../models');
|
||||||
|
|
||||||
|
//TODO: Handle missing images
|
||||||
|
module.exports = {
|
||||||
|
name: "Compositing",
|
||||||
|
renderProfile: async function(profile, svgTemplate, renderedCards) {
|
||||||
|
let hash = crypto.createHash('md5').update(JSON.stringify(profile)).digest('hex');
|
||||||
|
|
||||||
|
let outFile = `/app/assets/image_cache/profiles/${hash}.gif`;
|
||||||
|
console.log('Rendering profile to ' + outFile);
|
||||||
|
|
||||||
|
//composite {overlay} {background} [{mask}] [-compose {method}] {result}
|
||||||
|
let args = ['svg:-', 'null:',
|
||||||
|
'\(', `${renderedCards[0]}`, '-coalesce', '\)',
|
||||||
|
'-geometry', '+25+85', '-compose', 'over', '-layers', 'composite',
|
||||||
|
'null:', '\(', `${renderedCards[1]}`, '-coalesce', '-resize', '170x283', '\)',
|
||||||
|
'-geometry', '+350+300', '-compose', 'over', '-layers', 'composite',
|
||||||
|
'null:', '\(', `${renderedCards[2]}`, '-coalesce', '-resize', '170x283', '\)',
|
||||||
|
'-geometry', '+535+300', '-compose', 'over', '-layers', 'composite',
|
||||||
|
'null:', '\(', `${renderedCards[3]}`, '-coalesce', '-resize', '170x283', '\)',
|
||||||
|
'-geometry', '+720+300', '-compose', 'over', '-layers', 'composite',
|
||||||
|
'-layers', 'optimize', outFile];
|
||||||
|
|
||||||
|
console.log('GM Args: ' + args);
|
||||||
|
|
||||||
|
const composite = spawn('convert', args);
|
||||||
|
composite.stdin.write(svgTemplate);
|
||||||
|
composite.stdin.end();
|
||||||
|
|
||||||
|
composite.stderr.on('data', (data) => {
|
||||||
|
console.log(`stdout: ${data}`);
|
||||||
|
});
|
||||||
|
const exitCode = await new Promise( (resolve, reject) => {
|
||||||
|
composite.on('close', resolve);
|
||||||
|
})
|
||||||
|
|
||||||
|
return outFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user