This commit is contained in:
Blockzilla101 2023-02-06 19:58:25 +05:00
parent 0b3fef0fe4
commit 93abcdc48e
5 changed files with 326 additions and 1152 deletions

View file

@ -1,5 +1,5 @@
const { Citation } = require('./src/citation') const { Citation } = require('./src/citation')
const { registerFont } = require('canvas') const { GlobalFonts } = require('@napi-rs/canvas')
const path = require('path') const path = require('path')
const fs = require('fs') const fs = require('fs')
@ -11,6 +11,6 @@ if (!fs.existsSync(dataDir)) throw new Error(`${dataDir} is no where to be found
if (!fs.existsSync(fontFile)) throw new Error(`Font ${fontFile} is no where to be found`) if (!fs.existsSync(fontFile)) throw new Error(`Font ${fontFile} is no where to be found`)
if (!fs.existsSync(logo)) throw new Error(`Logo ${logo} is no where to be found`) if (!fs.existsSync(logo)) throw new Error(`Logo ${logo} is no where to be found`)
registerFont(fontFile, { family: 'BMmini' }); GlobalFonts.registerFromPath(path.join(__dirname, 'data', 'BMmini.ttf'), 'BMmini')
module.exports = { Citation } module.exports = { Citation }

1346
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,15 @@
{ {
"name": "@blockzilla101/citation", "name": "@blockzilla101/citation",
"version": "1.2.3", "version": "1.2.3",
"dependencies": { "dependencies": {
"canvas": "^2.6.1", "@napi-rs/canvas": "^0.1.34",
"gif-encoder-2": "^1.0.5" "args-parser": "^1.3.0",
}, "gif-encoder-2": "^1.0.5"
"author": "Blockzilla101", },
"main": "index.js", "author": "Blockzilla101",
"repository": { "main": "index.js",
"type": "git", "repository": {
"url": "https://github.com/Blockzilla101/citation" "type": "git",
} "url": "https://github.com/Blockzilla101/citation"
}
} }

View file

@ -1,4 +1,5 @@
const { createCanvas, Canvas, loadImage, Image } = require('canvas'); // const { createCanvas, Canvas, loadImage, Image } = require('canvas');
const { createCanvas, loadImage, Image, Canvas } = require('@napi-rs/canvas')
const { text, textWrapped, line, dottedLine, barcode, rect, textFitsHeight, wrap, tint } = require('./util'); const { text, textWrapped, line, dottedLine, barcode, rect, textFitsHeight, wrap, tint } = require('./util');
const Encoder = require('gif-encoder-2'); const Encoder = require('gif-encoder-2');
const fs = require('fs'); const fs = require('fs');
@ -146,9 +147,6 @@ module.exports.Citation = class {
this.#canvas = createCanvas(this.#width, this.#height); this.#canvas = createCanvas(this.#width, this.#height);
this.#ctx = this.#canvas.getContext('2d'); this.#ctx = this.#canvas.getContext('2d');
this.#ctx.imageSmoothingEnabled = false;
this.#ctx.antialias = 'none';
if (!this.#logo) this.#logo = await loadImage(`${__dirname + '/../data'}/logo.png`); if (!this.#logo) this.#logo = await loadImage(`${__dirname + '/../data'}/logo.png`);
} }
@ -161,7 +159,8 @@ module.exports.Citation = class {
*/ */
async render(out, gif = false, frameRate = 10, yPos = null) { async render(out, gif = false, frameRate = 10, yPos = null) {
await this.#draw() await this.#draw()
let data = gif ? await this.#animated(frameRate, yPos) : this.#canvas.toBuffer() const b = this.#canvas.toBuffer('image/png');
let data = gif ? await this.#animated(frameRate, yPos) : b
if (out) { if (out) {
fs.writeFileSync(out, data) fs.writeFileSync(out, data)
} }
@ -174,19 +173,19 @@ module.exports.Citation = class {
if (this.resizeReason) { if (this.resizeReason) {
let wrapped = wrap(this.reason, this.font, this.moaFt, this.#ctx, this.#reasonMaxWidth) let wrapped = wrap(this.reason, this.font, this.moaFt, this.#ctx, this.#reasonMaxWidth)
const ogHeight = this.#height const ogHeight = this.#height
let newHeight = this.#height;
if (!textFitsHeight(wrapped, this.font, this.#ctx, this.#reasonMaxHeight)) { if (!textFitsHeight(wrapped, this.font, this.#ctx, this.#reasonMaxHeight)) {
this.#ctx.font = this.font this.#ctx.font = this.font
let metrics = this.#ctx.measureText(wrapped); let metrics = this.#ctx.measureText(wrapped);
this.#canvas.height += (metrics.emHeightDescent - (metrics.emHeightAscent / 2)) - this.#reasonMaxHeight; newHeight += (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent + 2) * wrapped.split('\n').length - this.#reasonMaxHeight;
this.#height = this.#canvas.height
if (this.resizeLimit > ogHeight) { if (this.resizeLimit > ogHeight) {
if (this.#height > this.resizeLimit) { if (newHeight > this.resizeLimit) {
this.#canvas.height = this.resizeLimit newHeight = this.resizeLimit
this.#height = this.resizeLimit
} }
} }
} }
this.height = this.#canvas.height this.#height = newHeight
await this.#createCanvas()
} }
// Bg // Bg

View file

@ -1,6 +1,6 @@
const { NodeCanvasRenderingContext2D, createCanvas } = require('canvas'); const { createCanvas, SKRSContext2D } = require('@napi-rs/canvas');
/** @typedef {NodeCanvasRenderingContext2D} RenderingContext*/ /** @typedef {SKRSContext2D} RenderingContext*/
/** @typedef {string|CanvasGradient|CanvasPattern} Style*/ /** @typedef {string|CanvasGradient|CanvasPattern} Style*/
/** @typedef {"center"|"end"|"left"|"right"|"start"} TextAlignment*/ /** @typedef {"center"|"end"|"left"|"right"|"start"} TextAlignment*/
@ -44,7 +44,7 @@ function line(startX, startY,endX, endY, style , ctx, width = 1) {
} }
/** /**
* @param {string} text * @param {string} fillText
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @param {string} font * @param {string} font
@ -53,24 +53,27 @@ function line(startX, startY,endX, endY, style , ctx, width = 1) {
* @param {TextAlignment} [alignment="left"] * @param {TextAlignment} [alignment="left"]
* @param {number} [maxWidth=0] * @param {number} [maxWidth=0]
*/ */
function text(text, x, y, font, style, ctx, alignment = 'left', maxWidth) { function text(fillText, x, y, font, style, ctx, alignment = 'left', maxWidth) {
if (fillText.includes('\n')) {
const metrics = ctx.measureText(fillText)
const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent + 2
let currY = y;
const lines = fillText.split('\n')
for (const line of lines) {
text(line, x, currY, font, style, ctx, alignment, maxWidth)
currY += height
}
return
}
ctx.fillStyle = style ctx.fillStyle = style
ctx.strokeStyle = style
ctx.font = font; ctx.font = font;
ctx.textAlign = alignment ctx.textAlign = alignment
if (typeof maxWidth !== 'undefined') {
const metrics = ctx.measureText(text);
const size = metrics.emHeightAscent + metrics.emHeightDescent;
if (maxWidth < 0) maxWidth = 0; ctx.fillText(fillText, x, y, maxWidth);
let width = ctx.measureText(text).width;
while(width > maxWidth) {
if (width - maxWidth > maxWidth) text = text.substr(0, text.length - (width / size));
text = text.substr(0, text.length - 1);
width = ctx.measureText(text).width
}
}
ctx.fillText(text, x, y);
} }
/** /**
@ -86,40 +89,31 @@ function text(text, x, y, font, style, ctx, alignment = 'left', maxWidth) {
*/ */
function textWrapped(str, x, y, font, style, ctx, maxWidth, maxHeight, alignment = "left") { function textWrapped(str, x, y, font, style, ctx, maxWidth, maxHeight, alignment = "left") {
let newStr = wrap(str, font, style, ctx, maxWidth) let newStr = wrap(str, font, style, ctx, maxWidth)
if (typeof maxHeight !== 'undefined' && maxHeight > 0 && newStr.includes('\n')) {
let metrics = ctx.measureText(newStr);
let height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
while (height > maxHeight) {
newStr = newStr.substr(0, newStr.lastIndexOf('\n'));
metrics = ctx.measureText(newStr);
height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
}
}
text(newStr, x, y, font, style, ctx, alignment, maxWidth); text(newStr, x, y, font, style, ctx, alignment, maxWidth);
} }
function wrap(str, font, style, ctx, maxWidth) { function wrap(str, font, style, ctx, maxWidth) {
let words = str.split(" ");
let lines = [];
let currentLine = words[0];
ctx.font = font; ctx.font = font;
ctx.style = style; ctx.fillStyle = style
for (let i = 1; i < words.length; i++) { const lines = str.split('\n')
let word = words[i]; const newStr = []
let width = ctx.measureText(currentLine + " " + word).width; for (const line of lines) {
if (width < maxWidth) { const words = line.split(' ');
currentLine += " " + word; let currStr = []
} else { for (const word of words) {
lines.push(currentLine); currStr.push(word)
currentLine = word; if (ctx.measureText(currStr.join(' ')).width > maxWidth) {
const lastWord = currStr.pop()
newStr.push(currStr.join(' '))
currStr = []
currStr.push(lastWord)
}
} }
newStr.push(currStr.join(' ').trim())
} }
lines.push(currentLine);
return lines.join('\n'); return newStr.join('\n').trim()
} }
/** /**
@ -173,7 +167,7 @@ function textFitsWidth(text, font, ctx, maxWidth) {
function textFitsHeight(text, font, ctx, maxHeight) { function textFitsHeight(text, font, ctx, maxHeight) {
ctx.font = font; ctx.font = font;
let metrics = ctx.measureText(text); let metrics = ctx.measureText(text);
return metrics.emHeightDescent - (metrics.emHeightAscent / 2) <= maxHeight; return (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent + 2) * text.split('\n').length <= maxHeight;
} }
function tint(image, color, opacity = 0.5) { function tint(image, color, opacity = 0.5) {