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"?>
|
||||
<!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;">
|
||||
<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="BADGES" transform="matrix(0.511675,0,0,0.501053,-25.2132,12.3596)">
|
||||
<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;">
|
||||
<g id="BG" transform="matrix(2,0,0,2,-1.23161e-13,0)">
|
||||
<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);"/>
|
||||
<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"/>
|
||||
@@ -25,11 +27,11 @@
|
||||
</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)">
|
||||
<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">
|
||||
<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>
|
||||
<g clip-path="url(#_clip2)">
|
||||
<g transform="matrix(1.25332,0,0,0.899538,-99.307,18.6818)">
|
||||
@@ -45,9 +47,9 @@
|
||||
</g>
|
||||
</g>
|
||||
<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">
|
||||
<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>
|
||||
<g clip-path="url(#_clip3)">
|
||||
<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);"/>
|
||||
</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>
|
||||
</g>
|
||||
</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 { Card, User, Character } = require("../models");
|
||||
const UserUtils = require("../util/users");
|
||||
const { UserUtils, Compositing, Rendering } = require("../util");
|
||||
const fs = require('fs');
|
||||
|
||||
const pageSize = 8;
|
||||
|
||||
@@ -10,9 +11,26 @@ module.exports = {
|
||||
.setName("profile")
|
||||
.setDescription("View your profile"),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply();
|
||||
let user = await UserUtils.getUserByDiscordId(interaction.member.id);
|
||||
|
||||
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