182 lines
4.2 KiB (Stored with Git LFS)
TypeScript
182 lines
4.2 KiB (Stored with Git LFS)
TypeScript
import { createCanvas, loadImage, registerFont } from "canvas";
|
|
import { writeFileSync } from "fs";
|
|
import sharp from "sharp";
|
|
import { MiQOptions } from "./miq";
|
|
|
|
function maxLengthCut(
|
|
text: string,
|
|
maxLength: number,
|
|
) {
|
|
if (text.length > maxLength) {
|
|
text = text.substring(0, maxLength)
|
|
+ "...";
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
function autoLineBreak(
|
|
text: string,
|
|
maxWidth: number,
|
|
font: string = "48px Noto Sans JP"
|
|
): string {
|
|
const ctx = createCanvas(maxWidth, 100).getContext("2d");
|
|
ctx.font = font;
|
|
|
|
const lines: string[] = [];
|
|
let currentLine = "";
|
|
|
|
for (let i = 0; i < text.length; i++) {
|
|
const char = text[i];
|
|
const testLine = currentLine + char;
|
|
const width = ctx.measureText(testLine).width;
|
|
|
|
if (width > maxWidth) {
|
|
lines.push(currentLine);
|
|
currentLine = char;
|
|
|
|
while (ctx.measureText(currentLine).width > maxWidth && currentLine.length > 1) {
|
|
const cutPoint = currentLine.length - 1;
|
|
lines.push(currentLine.slice(0, cutPoint));
|
|
currentLine = currentLine.slice(cutPoint);
|
|
}
|
|
} else {
|
|
currentLine = testLine;
|
|
}
|
|
}
|
|
|
|
if (currentLine) lines.push(currentLine);
|
|
|
|
return lines.join("\n");
|
|
}
|
|
|
|
function textReplace(
|
|
text: string,
|
|
maxWidth: number
|
|
) {
|
|
text = text.replaceAll("\n", "");
|
|
text = maxLengthCut(text, 100);
|
|
text = autoLineBreak(text, maxWidth);
|
|
|
|
return text;
|
|
}
|
|
|
|
async function iconReplace(
|
|
color: boolean,
|
|
iconURL: string,
|
|
) {
|
|
let result = "";
|
|
|
|
const buffer = await(await fetch(iconURL, {
|
|
method: "GET",
|
|
cache: "no-store",
|
|
})).arrayBuffer();
|
|
|
|
if (color) {
|
|
const img = await sharp(Buffer.from(buffer))
|
|
.png()
|
|
.toBuffer();
|
|
|
|
result = `data:image/png;base64,${img.toString("base64")}`;
|
|
} else {
|
|
const img = await sharp(Buffer.from(buffer))
|
|
.png()
|
|
.grayscale()
|
|
.toBuffer();
|
|
|
|
result = `data:image/png;base64,${img.toString("base64")}`;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* A function to generate
|
|
* Make it a quote on Node.js.
|
|
*/
|
|
export default async function MiQ({
|
|
type,
|
|
color,
|
|
text,
|
|
iconURL,
|
|
userName,
|
|
userID,
|
|
}: MiQOptions) {
|
|
// フォント読み込み
|
|
registerFont("miq/fonts/NotoSansJP.ttf", { family: "Noto Sans JP" });
|
|
|
|
// 初期化
|
|
const canvas = createCanvas(1200, 630);
|
|
const ctx = canvas.getContext("2d");
|
|
|
|
// 背景描画
|
|
ctx.fillStyle = "black";
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// アイコン描画
|
|
const iconImg = await loadImage(await iconReplace(
|
|
color,
|
|
iconURL,
|
|
));
|
|
const iconSize = canvas.height;
|
|
ctx.drawImage(iconImg, 0, 0, iconSize, iconSize);
|
|
|
|
// ユーザー名描画
|
|
ctx.font = "38px Noto Sans JP";
|
|
ctx.fillStyle = "white";
|
|
ctx.textAlign = "center";
|
|
ctx.textBaseline = "middle";
|
|
let x = 910;
|
|
let y = 480;
|
|
ctx.fillText(`- ${userName}`, x, y, canvas.width-x);
|
|
|
|
// ユーザーID描画
|
|
ctx.font = "28px Noto Sans JP";
|
|
ctx.fillStyle = "#3c3c3c";
|
|
ctx.fillText(`@${userID}`, x, y+50, canvas.width-x);
|
|
|
|
// 本文描画
|
|
const maxWidth = canvas.width - iconSize;
|
|
text = textReplace(text, maxWidth);
|
|
ctx.font = "48px Noto Sans JP";
|
|
ctx.textAlign = "center";
|
|
ctx.textBaseline = "middle";
|
|
ctx.fillStyle = "white";
|
|
ctx.fillText(text, x, 80);
|
|
|
|
// フェード描画
|
|
const fadeColor = "black";
|
|
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
|
|
gradient.addColorStop(0, "rgba(0, 0, 0, 0)");
|
|
gradient.addColorStop(0.5, fadeColor);
|
|
ctx.fillStyle = gradient;
|
|
ctx.fillRect(0, 0, iconSize, canvas.height);
|
|
|
|
// 返答
|
|
if (type === "Buffer") {
|
|
return canvas.toBuffer();
|
|
} else if (type === "Base64Data") {
|
|
return canvas.toDataURL()
|
|
.replace("data:image/png;base64,", "");
|
|
} else if (type === "Base64URL") {
|
|
return canvas.toDataURL();
|
|
} else {
|
|
return "Error: The type property is invalid.";
|
|
}
|
|
}
|
|
|
|
/*(async () => {
|
|
writeFileSync(
|
|
"miq/a.png",
|
|
await MiQ({
|
|
type: "Buffer",
|
|
color: false,
|
|
text: "テスト",
|
|
iconURL: "https://media.uwuzu.net/uwuzu-net/files/3ov7dghkn7.webp",
|
|
userName: "Last2014",
|
|
userID: "last2014",
|
|
}),
|
|
"utf-8"
|
|
);
|
|
})()*/
|