UNPKG

@nmmty/lazycanvas

Version:

A simple way to interact with @napi-rs/canvas in an advanced way!

515 lines (514 loc) 22.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getBoundingBoxBezier = exports.centring = exports.isImageUrlValid = exports.generateRandomName = exports.saveFile = exports.transform = exports.parseFillStyle = exports.filters = exports.opacity = exports.drawShadow = exports.parser = exports.parseToNormal = exports.parseColor = exports.parseHex = exports.isColor = exports.generateID = void 0; const enum_1 = require("../types/enum"); const Gradient_1 = require("../structures/helpers/Gradient"); const LazyUtil_1 = require("./LazyUtil"); const fs = __importStar(require("fs")); const jimp = __importStar(require("jimp")); const Pattern_1 = require("../structures/helpers/Pattern"); const Link_1 = require("../structures/helpers/Link"); const Group_1 = require("../structures/components/Group"); const LineLayer_1 = require("../structures/components/LineLayer"); const BezierLayer_1 = require("../structures/components/BezierLayer"); const QuadraticLayer_1 = require("../structures/components/QuadraticLayer"); const TextLayer_1 = require("../structures/components/TextLayer"); function generateID(type) { return `${type}-${Math.random().toString(36).substr(2, 9)}`; } exports.generateID = generateID; let percentReg = /^(\d+)%$/; let pxReg = /^(\d+)px$/; let canvasReg = /^(vw|vh|vmin|vmax)$/; let linkReg = /^(link-w|link-h|link-x|link-y)-([A-Za-z0-9_]+)-(\d+)$/; let hexReg = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/; let rgbReg = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/; let rgbaReg = /^rgba\((\d+),\s*(\d+),\s*(\d+),\s*(0|0?\.\d+|1(\.0)?)\)$/; let hslReg = /^hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)$/; let hslaReg = /^hsla\((\d+),\s*(\d+)%,\s*(\d+)%,\s*(0|0?\.\d+|1(\.0)?)\)$/; function isColor(v) { return typeof (v === 'string' && (hexReg.test(v) || rgbReg.test(v) || rgbaReg.test(v) || hslReg.test(v) || hslaReg.test(v))) || v instanceof Gradient_1.Gradient || v instanceof Pattern_1.Pattern; } exports.isColor = isColor; function tooShort(v) { return v.length < 2 ? `0${v}` : v; } function parseHex(v) { if (hexReg.test(v)) { return v; } else if (rgbReg.test(v)) { let match = v.match(rgbReg); let r = parseInt(match[1]); let g = parseInt(match[2]); let b = parseInt(match[3]); return `#${tooShort(r.toString(16))}${tooShort(g.toString(16))}${tooShort(b.toString(16))}`; } else if (rgbaReg.test(v)) { let match = v.match(rgbaReg); let r = parseInt(match[1]); let g = parseInt(match[2]); let b = parseInt(match[3]); let a = parseFloat(match[4]); return `#${tooShort(r.toString(16))}${tooShort(g.toString(16))}${tooShort(b.toString(16))}:${a}`; } else if (hslReg.test(v)) { let match = v.match(hslReg); let h = parseInt(match[1]); let s = parseInt(match[2]); let l = parseInt(match[3]); l /= 100; const b = s * Math.min(l, 1 - l) / 100; const f = (n) => { const k = (n + h / 30) % 12; const color = l - b * Math.max(Math.min(k - 3, 9 - k, 1), -1); return Math.round(255 * color).toString(16).padStart(2, '0'); }; return `#${f(0)}${f(8)}${f(4)}`; } else if (hslaReg.test(v)) { let match = v.match(hslaReg); let h = parseInt(match[1]); let s = parseInt(match[2]); let l = parseInt(match[3]); let a = parseFloat(match[4]); l /= 100; const b = s * Math.min(l, 1 - l) / 100; const f = (n) => { const k = (n + h / 30) % 12; const color = l - b * Math.max(Math.min(k - 3, 9 - k, 1), -1); return Math.round(255 * color).toString(16).padStart(2, '0'); }; return `#${f(0)}${f(8)}${f(4)}:${a}`; } else { return '#000000'; } } exports.parseHex = parseHex; function parseColor(v) { if (typeof v === 'string') { return parseHex(v); } else if (v instanceof Gradient_1.Gradient || v instanceof Pattern_1.Pattern) { return v; } else { return '#000000'; } } exports.parseColor = parseColor; function parseToNormal(v, ctx, canvas, layer = { width: 0, height: 0 }, options = { vertical: false, layer: false }, manager) { if (typeof v === 'number') { return v; } else if (typeof v === 'string') { if (percentReg.test(v)) { return (parseFloat(v) / 100) * (options.layer ? (options.vertical ? layer.width : layer.height) : (options.vertical ? canvas.width : canvas.height)); } else if (pxReg.test(v)) { return parseFloat(v); } else if (canvasReg.test(v)) { if (v === 'vw') { return (options.layer ? layer.width : canvas.width); } else if (v === 'vh') { return (options.layer ? layer.height : canvas.height); } else if (v === 'vmin') { return (options.layer ? Math.max(layer.width, layer.height) : Math.min(canvas.width, canvas.height)); } else if (v === 'vmax') { return (options.layer ? Math.max(layer.width, layer.height) : Math.max(canvas.width, canvas.height)); } } else if (linkReg.test(v)) { let match = v.match(linkReg); if (!manager) return 0; let anyLayer = manager.get(match[2], true); const parcer = parser(ctx, canvas, manager); switch (match[1]) { case 'link-w': if (anyLayer && !(anyLayer instanceof Group_1.Group)) { if (anyLayer instanceof LineLayer_1.LineLayer || anyLayer instanceof BezierLayer_1.BezierLayer || anyLayer instanceof QuadraticLayer_1.QuadraticLayer) { return anyLayer.getBoundingBox(ctx, canvas, manager).width + (parseInt(match[3]) || 0); } else if (anyLayer instanceof TextLayer_1.TextLayer) { return anyLayer.measureText(ctx, canvas).width + (parseInt(match[3]) || 0); } else { return (parcer.parse(anyLayer.props.size.width) || 0) + (parseInt(match[3]) || 0); } } else { return 0; } break; case 'link-h': if (anyLayer && !(anyLayer instanceof Group_1.Group)) { if (anyLayer instanceof LineLayer_1.LineLayer || anyLayer instanceof BezierLayer_1.BezierLayer || anyLayer instanceof QuadraticLayer_1.QuadraticLayer) { return anyLayer.getBoundingBox(ctx, canvas, manager).height + (parseInt(match[3]) || 0); } else if (anyLayer instanceof TextLayer_1.TextLayer) { return anyLayer.measureText(ctx, canvas).height + (parseInt(match[3]) || 0); } else { return (parcer.parse(anyLayer.props.size.height, LazyUtil_1.defaultArg.wh(parcer.parse(anyLayer.props.size.width)), LazyUtil_1.defaultArg.vl(true)) || 0) + (parseInt(match[3]) || 0); } } else { return 0; } break; case 'link-x': if (anyLayer && !(anyLayer instanceof Group_1.Group)) { return (parcer.parse(anyLayer.props.x) || 0) + (parseInt(match[3]) || 0); } else { return 0; } break; case 'link-y': if (anyLayer && !(anyLayer instanceof Group_1.Group)) { return (parcer.parse(anyLayer.props.y, LazyUtil_1.defaultArg.wh(), LazyUtil_1.defaultArg.vl(true)) || 0) + (parseInt(match[3]) || 0); } else { return 0; } break; } } } else if (v instanceof Link_1.Link) { if (!manager) return 0; let anyLayer = manager.get(v.source, true); const parcer = parser(ctx, canvas, manager); switch (v.type) { case enum_1.LinkType.Width: case 'width': if (anyLayer && !(anyLayer instanceof Group_1.Group)) { if (anyLayer instanceof LineLayer_1.LineLayer || anyLayer instanceof BezierLayer_1.BezierLayer || anyLayer instanceof QuadraticLayer_1.QuadraticLayer) { return anyLayer.getBoundingBox(ctx, canvas, manager).width + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0); } else if (anyLayer instanceof TextLayer_1.TextLayer) { return anyLayer.measureText(ctx, canvas).width + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0); } else { return (parcer.parse(anyLayer.props.size.width) || 0) + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0); } } else { return 0; } break; case enum_1.LinkType.Height: case 'height': if (anyLayer && !(anyLayer instanceof Group_1.Group)) { if (anyLayer instanceof LineLayer_1.LineLayer || anyLayer instanceof BezierLayer_1.BezierLayer || anyLayer instanceof QuadraticLayer_1.QuadraticLayer) { return anyLayer.getBoundingBox(ctx, canvas, manager).height + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0); } else if (anyLayer instanceof TextLayer_1.TextLayer) { return anyLayer.measureText(ctx, canvas).height + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0); } else { return (parcer.parse(anyLayer.props.size.height, LazyUtil_1.defaultArg.wh(parcer.parse(anyLayer.props.size.width)), LazyUtil_1.defaultArg.vl(true)) || 0) + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0); } } else { return 0; } break; case enum_1.LinkType.X: case 'x': if (anyLayer && !(anyLayer instanceof Group_1.Group)) { return (parcer.parse(anyLayer.props.x) || 0) + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0); } else { return 0; } break; case enum_1.LinkType.Y: case 'y': if (anyLayer && !(anyLayer instanceof Group_1.Group)) { return (parcer.parse(anyLayer.props.y) || 0) + (parcer.parse(v.additionalSpacing, LazyUtil_1.defaultArg.wh(layer.width, layer.height), LazyUtil_1.defaultArg.vl(options.vertical, options.layer)) || 0); } else { return 0; } break; default: return 0; } } else { return 0; } return 0; } exports.parseToNormal = parseToNormal; function parser(ctx, canvas, manager) { return { parse(v, layer = LazyUtil_1.defaultArg.wh(), options = LazyUtil_1.defaultArg.vl()) { return parseToNormal(v, ctx, canvas, layer, options, manager); }, parseBatch(values) { const result = {}; for (const key in values) { const { v, layer, options } = values[key]; result[key] = parseToNormal(v, ctx, canvas, layer ?? LazyUtil_1.defaultArg.wh(), options ?? LazyUtil_1.defaultArg.vl(), manager); } return result; } }; } exports.parser = parser; function drawShadow(ctx, shadow) { if (shadow) { ctx.shadowColor = shadow.color; ctx.shadowBlur = shadow.blur || 0; ctx.shadowOffsetX = shadow.offsetX || 0; ctx.shadowOffsetY = shadow.offsetY || 0; } } exports.drawShadow = drawShadow; function opacity(ctx, opacity) { if (opacity < 1) { ctx.globalAlpha = opacity; } } exports.opacity = opacity; function filters(ctx, filters) { if (filters) { ctx.filter = filters; } } exports.filters = filters; function parseFillStyle(ctx, color) { if (color instanceof Gradient_1.Gradient || color instanceof Pattern_1.Pattern) { return color.draw(ctx); } else { return color; } } exports.parseFillStyle = parseFillStyle; function transform(ctx, transform, layer = { width: 0, height: 0, x: 0, y: 0, type: enum_1.LayerType.Morph }, extra = { text: '', textAlign: enum_1.TextAlign.Left, fontSize: 0, multiline: false }) { if (transform) { if (transform.translate) { ctx.translate(transform.translate.x, transform.translate.y); } if (transform.rotate) { switch (layer.type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: case enum_1.LayerType.BezierCurve: case enum_1.LayerType.QuadraticCurve: case enum_1.LayerType.Line: ctx.translate(layer.x + (layer.width / 2), layer.y + (layer.height / 2)); ctx.rotate((Math.PI / 180) * transform.rotate); ctx.translate(-(layer.x + (layer.width / 2)), -(layer.y + (layer.height / 2))); break; case enum_1.LayerType.Text: if (extra.multiline) { ctx.translate(layer.x + (layer.width / 2), layer.y + (layer.height / 2)); ctx.rotate((Math.PI / 180) * transform.rotate); ctx.translate(-(layer.x + (layer.width / 2)), -(layer.y + (layer.height / 2))); } else { if (extra.textAlign === enum_1.TextAlign.Center) { ctx.translate(layer.x, layer.y); ctx.rotate((Math.PI / 180) * transform.rotate); ctx.translate(-layer.x, -layer.y); } else if (extra.textAlign === enum_1.TextAlign.Left || extra.textAlign === enum_1.TextAlign.Start) { ctx.translate(layer.x + (extra.fontSize * extra.text.length) / 2, layer.y); ctx.rotate((Math.PI / 180) * transform.rotate); ctx.translate(-(layer.x + (extra.fontSize * extra.text.length) / 2), -layer.y); } else if (extra.textAlign === enum_1.TextAlign.Right || extra.textAlign === enum_1.TextAlign.End) { ctx.translate(layer.x - (extra.fontSize * extra.text.length) / 2, layer.y); ctx.rotate((Math.PI / 180) * transform.rotate); ctx.translate(-(layer.x - (extra.fontSize * extra.text.length) / 2), -layer.y); } } break; } } if (transform.scale) { ctx.scale(transform.scale.x, transform.scale.y); } if (transform.matrix) { if (!transform.matrix.a || !transform.matrix.b || !transform.matrix.c || !transform.matrix.d || !transform.matrix.e || !transform.matrix.f) throw new LazyUtil_1.LazyError('The matrix transformation must be a valid matrix'); ctx.transform(transform.matrix.a, transform.matrix.b, transform.matrix.c, transform.matrix.d, transform.matrix.e, transform.matrix.f); } } } exports.transform = transform; async function saveFile(buffer, extension, name) { if (!buffer) throw new LazyUtil_1.LazyError('Buffer must be provided'); if (!extension) throw new LazyUtil_1.LazyError('Extension must be provided'); fs.writeFileSync(`${name === undefined ? generateRandomName() : name}.${extension}`, buffer); } exports.saveFile = saveFile; function generateRandomName() { return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); } exports.generateRandomName = generateRandomName; function isImageUrlValid(src) { try { jimp.read(src); return true; } catch (error) { return false; } } exports.isImageUrlValid = isImageUrlValid; function centring(centring, type, width, height, x, y) { switch (centring) { case enum_1.Centring.Center: case "center": switch (type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: x -= width / 2; y -= height / 2; break; } return { x, y }; case enum_1.Centring.CenterBottom: case "center-bottom": switch (type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: x -= width / 2; break; } return { x, y }; case enum_1.Centring.CenterTop: case "center-top": switch (type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: x -= width / 2; y -= height; break; } return { x, y }; case enum_1.Centring.Start: case "start": switch (type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: y -= height / 2; break; } return { x, y }; case enum_1.Centring.StartBottom: case "start-bottom": return { x, y }; case enum_1.Centring.StartTop: case "start-top": switch (type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: y -= height; break; } return { x, y }; case enum_1.Centring.End: case "end": switch (type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: x -= width; y -= height / 2; break; } return { x, y }; case enum_1.Centring.EndBottom: case "end-bottom": switch (type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: x -= width; break; } return { x, y }; case enum_1.Centring.EndTop: case "end-top": switch (type) { case enum_1.LayerType.Image: case enum_1.LayerType.Morph: x -= width; y -= height; break; } return { x, y }; case enum_1.Centring.None: case "none": return { x, y }; } } exports.centring = centring; function quadraticBezier(p0, p1, p2, t) { const x = (1 - t) ** 2 * p0.x + 2 * (1 - t) * t * p1.x + t ** 2 * p2.x; const y = (1 - t) ** 2 * p0.y + 2 * (1 - t) * t * p1.y + t ** 2 * p2.y; return { x, y }; } function cubicBezier(p0, p1, p2, p3, t) { const x = (1 - t) ** 3 * p0.x + 3 * (1 - t) ** 2 * t * p1.x + 3 * (1 - t) * t ** 2 * p2.x + t ** 3 * p3.x; const y = (1 - t) ** 3 * p0.y + 3 * (1 - t) ** 2 * t * p1.y + 3 * (1 - t) * t ** 2 * p2.y + t ** 3 * p3.y; return { x, y }; } function getBoundingBoxBezier(points, steps = 100) { let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (let i = 0; i <= steps; i++) { const t = i / steps; let p; if (points.length === 3) { p = quadraticBezier(points[0], points[1], points[2], t); } else if (points.length === 4) { p = cubicBezier(points[0], points[1], points[2], points[3], t); } else { throw new LazyUtil_1.LazyError("Invalid number of points"); } minX = Math.min(minX, p.x); minY = Math.min(minY, p.y); maxX = Math.max(maxX, p.x); maxY = Math.max(maxY, p.y); } return { min: { x: minX, y: minY }, max: { x: maxX, y: maxY }, center: { x: (minX + maxX) / 2, y: (minY + maxY) / 2 }, width: maxX - minX, height: maxY - minY }; } exports.getBoundingBoxBezier = getBoundingBoxBezier;