diff --git a/commands/edit-image/font.js b/commands/edit-image/font.js new file mode 100644 index 00000000..545e0ace --- /dev/null +++ b/commands/edit-image/font.js @@ -0,0 +1,49 @@ +const Command = require('../../structures/Command'); +const { createCanvas } = require('canvas'); +const { wrapText } = require('../../util/Canvas'); + +module.exports = class FontCommand extends Command { + constructor(client) { + super(client, { + name: 'font', + aliases: ['font-test'], + group: 'edit-image', + memberName: 'font', + description: 'Types text in a specific font.', + args: [ + { + key: 'font', + prompt: 'What font do you want to use? Only fonts used in other commands are available.', + type: 'font' + }, + { + key: 'text', + prompt: 'What text do you want to type?', + type: 'string' + } + ] + }); + } + + run(msg, { font, text }) { + return msg.say({ files: [{ attachment: this.generateImage(font, text), name: `${font.filenameNoExt}.png` }] }); + } + + generateImage(font, text) { + const canvasPre = createCanvas(1, 1); + const ctxPre = canvasPre.getContext('2d'); + ctxPre.font = this.client.fonts.get(font.filename).toCanvasString(75); + const len = ctx.measureText(text); + const lines = wrapText(ctxPre, text, 450); + const canvas = createCanvas(Math.min(len, 500), 50 + (75 * lines.length)); + const ctx = canvas.getContext('2d'); + ctx.font = this.client.fonts.get(font.filename).toCanvasString(75); + ctx.textBaseline = 'middle'; + ctx.textAlign = 'center'; + ctx.fillStyle = 'white'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = 'black'; + ctx.fillText(lines.join('\n'), canvas.width / 2, canvas.height / 2); + return canvas.toBuffer(); + } +}; diff --git a/commands/games-sp/typing-test.js b/commands/games-sp/typing-test.js index 62c498b3..d3ef7b90 100644 --- a/commands/games-sp/typing-test.js +++ b/commands/games-sp/typing-test.js @@ -67,7 +67,7 @@ module.exports = class TypingTestCommand extends Command { generateImage(sentence) { const canvasPre = createCanvas(1, 1); const ctxPre = canvasPre.getContext('2d'); - ctxPre.font = '75px Noto'; + ctxPre.font = this.client.fonts.get('Noto-Regular.ttf').toCanvasString(75); const len = ctxPre.measureText(sentence); const canvas = createCanvas(100 + len.width, 200); const ctx = canvas.getContext('2d'); diff --git a/package.json b/package.json index a19d6593..23d9a3fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "129.7.3", + "version": "129.8.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { diff --git a/structures/Font.js b/structures/Font.js index 5f092c73..1e311a72 100644 --- a/structures/Font.js +++ b/structures/Font.js @@ -40,4 +40,8 @@ module.exports = class Font { get filenameNoExt() { return this.filename.replace(/(\.(otf|ttf))$/, ''); } + + get isVariant() { + return variants[this.filename]; + } }; diff --git a/types/font.js b/types/font.js new file mode 100644 index 00000000..4fa56dc8 --- /dev/null +++ b/types/font.js @@ -0,0 +1,48 @@ +const { ArgumentType, util: { disambiguation } } = require('discord.js-commando'); +const { escapeMarkdown } = require('discord.js'); + +module.exports = class FontArgumentType extends ArgumentType { + constructor(client) { + super(client, 'font'); + } + + validate(value) { + const choice = value.toLowerCase(); + let found = this.client.fonts.filter(font => { + if (font.isVariant) return false; + if (font.name.toLowerCase().includes(choice)) return true; + if (font.filenameNoExt.toLowerCase().includes(choice)) return true; + return false; + }); + if (found.size === 0) return false; + if (found.size === 1) return true; + const foundExact = found.filter(font => { + if (font.name.toLowerCase() === choice) return true; + if (font.filenameNoExt.toLowerCase() === choice) return true; + return false; + }); + if (foundExact.size === 1) return true; + if (foundExact.size > 0) found = foundExact; + return found.size <= 15 ? + `${disambiguation(found.map(font => escapeMarkdown(font.name)), 'fonts', null)}\n` : + 'Multiple fonts found. Please be more specific.'; + } + + parse(value) { + const choice = value.toLowerCase(); + let found = this.client.fonts.filter(font => { + if (font.name.toLowerCase().includes(choice)) return true; + if (font.filenameNoExt.toLowerCase().includes(choice)) return true; + return true; + }); + if (found.size === 0) return null; + if (found.size === 1) return found.first(); + const foundExact = found.filter(font => { + if (font.name.toLowerCase() === choice) return true; + if (font.filenameNoExt.toLowerCase() === choice) return true; + return true; + }); + if (foundExact.size === 1) return foundExact.first(); + return null; + } +};