+ const fetch = require('node-fetch');
const path = require('path');
const { createCanvas, loadImage, registerFont } = require('canvas');
@@ -84,7 +112,7 @@ const infoTextSpacing = 50;
const xivApiSupportedLanguages = ['en', 'ja', 'de', 'fr'];
const languageStrings = {
en: {
- raceAndClan: 'Race & Clan',
+ raceAndClan: 'Race & Clan',
guardian: 'Guardian',
grandCompany: 'Grand Company',
freeCompany: 'Free Company',
@@ -96,7 +124,7 @@ const languageStrings = {
minions: 'Minions',
},
de: {
- raceAndClan: 'Volk & Stamm',
+ raceAndClan: 'Volk & Stamm',
guardian: 'Schutzgott',
grandCompany: 'Staatliche Gesellschaft',
freeCompany: 'Freie Gesellschaft',
@@ -116,7 +144,7 @@ class CardCreator {
* @param {string} [xivApiKey] The API key for the XIV API to be used in all requests.
*/
constructor(xivApiKey = undefined) {
- this.xivApiKey = typeof xivApiKey === 'string' && xivApiKey !== '' ? xivApiKey : undefined;
+ this.xivApiKey = typeof xivApiKey === 'string' && xivApiKey !== '' ? xivApiKey : undefined;
this.initPromise = null;
}
@@ -220,11 +248,11 @@ class CardCreator {
const pixelData = imageData.data;
// Iterate over all pixels, where one consists of 4 numbers: r, g, b and a
- for (let index = 0; index < pixelData.length; index += 4) {
+ for (let index = 0; index < pixelData.length; index += 4) {
const [r, g, b] = pixelData.slice(index, index + 3);
// If the pixel is a special grey, change it to be transparent (a = 0)
- if (r == 64 && g == 64 && b == 64) {
+ if (r == 64 && g == 64 && b == 64) {
pixelData[index] = 0;
pixelData[index + 1] = 0;
pixelData[index + 2] = 0;
@@ -254,7 +282,7 @@ class CardCreator {
}
// If there is no OffHand, the MainHand item level counts twice
- if (gearset.Offhand != null && typeof gearset.MainHand != 'number') {
+ if (gearset.Offhand != null && typeof gearset.MainHand != 'number') {
const piece = gearset.MainHand;
// If this item is a special one, increase the total item level by only 1
@@ -302,7 +330,7 @@ class CardCreator {
* fs.writeFile('./test.png', png, err => {
* if (err) console.error(err);
* });
- * @returns {Promise} A promise representating the construction of the card's image data.
+ * @returns {Promise<Buffer>} A promise representating the construction of the card's image data.
*/
async createCard(characterId, customImage, language = 'en') {
const supportedLanguage = xivApiSupportedLanguages.includes(language) ? language : 'en';
@@ -321,7 +349,7 @@ class CardCreator {
characterInfoUrl.searchParams.set('extended', '1');
characterInfoUrl.searchParams.set('data', 'FC,MIMO');
characterInfoUrl.searchParams.set('columns', neededFields.join(','));
- if (typeof this.xivApiKey === 'string' && this.xivApiKey !== '') url.searchParams.set('private_key', this.xivApiKey);
+ if (typeof this.xivApiKey === 'string' && this.xivApiKey !== '') url.searchParams.set('private_key', this.xivApiKey);
const dataPromise = fetch(characterInfoUrl)
// Retry once if the request fails
@@ -331,7 +359,7 @@ class CardCreator {
const customImagePromise = customImage != null ? loadImage(customImage) : Promise.resolve();
const portraitPromise = dataPromise.then(data => loadImage(data.Character.Portrait));
const deityPromise = dataPromise.then(data => loadImage(`https://xivapi.com/${data.Character.GuardianDeity.Icon}`));
- const gcRankPromise = dataPromise.then(data => data.Character.GrandCompany.Company != null && data.Character.GrandCompany.Company.Name != null ? loadImage(`https://xivapi.com/${data.Character.GrandCompany.Rank.Icon}`) : null);
+ const gcRankPromise = dataPromise.then(data => data.Character.GrandCompany.Company != null && data.Character.GrandCompany.Company.Name != null ? loadImage(`https://xivapi.com/${data.Character.GrandCompany.Rank.Icon}`) : null);
const fcCrestPromise = dataPromise.then(data => data.Character.FreeCompanyName != null ? this.createCrest(data.FreeCompany.Crest) : null);
// Build canvas and only await data, when actually needed
@@ -355,15 +383,15 @@ class CardCreator {
ctx.fillRect(rectStartX, rectStartRow2Y, rectHalfWidth, rectHeightRow2); // Mounts
ctx.fillRect(rectStartXHalf, rectStartRow2Y, rectHalfWidth, rectHeightRow2); // Minions
ctx.fillRect(rectStartX, rectStartRow3Y, rectFullWidth, rectHeightRow3); // Character information
- ctx.fillRect(rectStartX, rectStartRow4Y, rectFullWidth, rectHeightRow4); // Eureka & Bozja
- ctx.fillRect(rectStartX, rectStartRow5Y, rectFullWidth, rectHeightRow5); // Classes & Jobs
+ ctx.fillRect(rectStartX, rectStartRow4Y, rectFullWidth, rectHeightRow4); // Eureka & Bozja
+ ctx.fillRect(rectStartX, rectStartRow5Y, rectFullWidth, rectHeightRow5); // Classes & Jobs
ctx.restore(); ctx.save();
// Draw non data dependent text
ctx.textAlign = 'left';
ctx.font = small;
ctx.fillStyle = primary;
- ctx.fillText(strings.raceAndClan, 480, infoTextSmallStartY); // Race & Clan
+ ctx.fillText(strings.raceAndClan, 480, infoTextSmallStartY); // Race & Clan
ctx.fillText(strings.guardian, 480, infoTextSmallStartY + infoTextSpacing); // Guardian
ctx.fillText(strings.elementalLevel, 480, 425); // Elemental level
ctx.fillText(strings.resistanceRank, 480, 475); // Resistance rank
@@ -421,7 +449,7 @@ class CardCreator {
if (Character.Title.Name != null) ctx.fillText(Character.Title.Name, 450, 40); // Character title
ctx.font = small;
- ctx.fillText(`${Character.Server} (${Character.DC})`, 450, 100); // Character service & DC
+ ctx.fillText(`${Character.Server} (${Character.DC})`, 450, 100); // Character service & DC
ctx.font = large;
ctx.fillStyle = white;
@@ -437,7 +465,7 @@ class CardCreator {
ctx.restore(); ctx.save();
}
- // Mounts & Minions
+ // Mounts & Minions
{
let minionsPercentage = "N/A"
let mountsPercentage = "N/A"
@@ -466,10 +494,10 @@ class CardCreator {
{
ctx.font = smed;
ctx.fillStyle = white;
- ctx.fillText(`${Character.Race.Name}, ${Character.Tribe.Name}`, 480, infoTextBigStartY); // Race & Clan
+ ctx.fillText(`${Character.Race.Name}, ${Character.Tribe.Name}`, 480, infoTextBigStartY); // Race & Clan
ctx.fillText(Character.GuardianDeity.Name, 480, infoTextBigStartY + infoTextSpacing); // Guardian
- if (Character.GrandCompany.Company != null && Character.GrandCompany.Company.Name != null) {
+ if (Character.GrandCompany.Company != null && Character.GrandCompany.Company.Name != null) {
ctx.font = small;
ctx.fillStyle = primary;
ctx.fillText(strings.grandCompany, 480, infoTextSmallStartY + infoTextSpacing * 2); // Grand Company
@@ -496,7 +524,7 @@ class CardCreator {
ctx.restore(); ctx.save();
}
- // Eureka & Bozja
+ // Eureka & Bozja
{
ctx.font = smed;
ctx.fillStyle = white;
@@ -505,7 +533,7 @@ class CardCreator {
ctx.restore(); ctx.save();
}
- // Classes & Jobs - data dependant job or class icons
+ // Classes & Jobs - data dependant job or class icons
{
const { ClassJobs } = Character;
ctx.drawImage(this.classOrJobIcon(ClassJobs[0], 19, 'gladiator', 'paladin'), 470, jobsRowIcon1Y, 30, 30); // Gladiator/Paladin
@@ -519,7 +547,7 @@ class CardCreator {
ctx.drawImage(this.classOrJobIcon(ClassJobs[17], 27, 'arcanist', 'summoner'), 680, jobsRowIcon2Y, 30, 30); // Summoner/Arcanist
}
- // Classes & Jobs - levels
+ // Classes & Jobs - levels
{
ctx.textAlign = 'center';
ctx.font = small;
@@ -615,3 +643,26 @@ class CardCreator {
}
exports.CardCreator = CardCreator;
+
+
+