UNPKG

ultimate-text-to-image

Version:

Generate UTF8 texts into image with auto line break for all international language, including Chinese, Japanese, Korean, etc..

249 lines (248 loc) 11.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.drawTexts = exports.drawImages = exports.drawBorder = exports.drawBackgroundColor = exports.renderHook = void 0; const canvas_1 = require("canvas"); const index_1 = require("./index"); /** @internal */ function renderHook(canvas, hook) { if (hook) { hook(canvas); } } exports.renderHook = renderHook; /** @internal */ function drawBackgroundColor(ctx, options) { if (options.color || typeof options.color === "number") { const { width, height } = ctx.canvas; ctx.fillStyle = (0, index_1.parseColorString)(options.color); ctx.fillRect(0, 0, width, height); } } exports.drawBackgroundColor = drawBackgroundColor; /** @internal */ function drawBorder(ctx, options) { if (options.size && (options.color || typeof options.color === "number")) { const { width, height } = ctx.canvas; ctx.strokeStyle = (0, index_1.parseColorString)(options.color); // multiple by 2 since it's align to center ctx.lineWidth = options.size * 2; ctx.strokeRect(0, 0, width, height); } } exports.drawBorder = drawBorder; /** @internal */ function drawImages(ctx, options) { const { images } = options; const canvasWidth = options.width | 0; const canvasHeight = options.height | 0; for (const imageSetting of images) { const { canvasImage, repeat, sx, sy, tx, ty } = imageSetting; const layer = imageSetting.layer || 0; // make sure it's a valid image if (!canvasImage || !canvasImage.width || !canvasImage.height) { continue; } // check the layer if (layer !== options.layer) { continue; } // prepare all dimensions const imageWidth = canvasImage.width; const imageHeight = canvasImage.height; const tempWidth = imageSetting.width === "image" ? imageWidth : (imageSetting.width === "canvas" ? canvasWidth : (imageSetting.width || 0)); const tempHeight = imageSetting.height === "image" ? imageHeight : (imageSetting.height === "canvas" ? canvasHeight : (imageSetting.height || 0)); const x = typeof sx === "number" ? (sx >= 0 ? sx : canvasWidth + sx) : (imageSetting.x || 0); const y = typeof sy === "number" ? (sy >= 0 ? sy : canvasHeight + sy) : (imageSetting.y || 0); const width = typeof tx === "number" ? (tx > 0 ? tx : canvasWidth + tx) - x : (tempWidth || canvasWidth - x); const height = typeof ty === "number" ? (ty > 0 ? ty : canvasHeight + ty) - y : (tempHeight || canvasHeight - y); // skip if invalid if (width <= 0 || height <= 0) { continue; } if (!repeat || repeat === "none" || repeat === "topLeft") { ctx.drawImage(canvasImage, 0, 0, width, height, x, y, width, height); } else if (repeat === "topCenter") { const sx1 = (imageWidth - width) / 2; ctx.drawImage(canvasImage, sx1, 0, width, height, x, y, width, height); } else if (repeat === "topRight") { const sx1 = imageWidth - width; ctx.drawImage(canvasImage, sx1, 0, width, height, x, y, width, height); } else if (repeat === "middleLeft") { const sy1 = (imageHeight - height) / 2; ctx.drawImage(canvasImage, 0, sy1, width, height, x, y, width, height); } else if (repeat === "center") { const sx1 = (imageWidth - width) / 2; const sy1 = (imageHeight - height) / 2; ctx.drawImage(canvasImage, sx1, sy1, width, height, x, y, width, height); } else if (repeat === "middleRight") { const sx1 = imageWidth - width; const sy1 = (imageHeight - height) / 2; ctx.drawImage(canvasImage, sx1, sy1, width, height, x, y, width, height); } else if (repeat === "bottomLeft") { const sy1 = imageHeight - height; ctx.drawImage(canvasImage, 0, sy1, width, height, x, y, width, height); } else if (repeat === "bottomCenter") { const sx1 = (imageWidth - width) / 2; const sy1 = imageHeight - height; ctx.drawImage(canvasImage, sx1, sy1, width, height, x, y, width, height); } else if (repeat === "bottomRight") { const sx1 = imageWidth - width; const sy1 = imageHeight - height; ctx.drawImage(canvasImage, sx1, sy1, width, height, x, y, width, height); } else if (repeat === "fit") { ctx.drawImage(canvasImage, 0, 0, imageWidth, imageHeight, x, y, width, height); } else if (repeat === "fitX") { const finalImageHeight = Math.min(height, imageHeight); ctx.drawImage(canvasImage, 0, 0, imageWidth, finalImageHeight, x, y, width, finalImageHeight); } else if (repeat === "fitY") { const finalImageWidth = Math.min(x + width - x, imageWidth); ctx.drawImage(canvasImage, 0, 0, finalImageWidth, imageHeight, x, y, finalImageWidth, height); } else if (repeat === "repeat") { for (let y1 = y; y1 < y + height; y1 += imageHeight) { for (let x1 = x; x1 < x + width; x1 += imageWidth) { const finalImageWidth = Math.min(x + width - x1, imageWidth); const finalImageHeight = Math.min(y + height - y1, imageHeight); ctx.drawImage(canvasImage, 0, 0, finalImageWidth, finalImageHeight, x1, y1, finalImageWidth, finalImageHeight); } } } else if (repeat === "repeatY") { for (let y1 = y; y1 < y + height; y1 += imageHeight) { const finalImageWidth = Math.min(x + width - x, imageWidth); const finalImageHeight = Math.min(y + height - y1, imageHeight); ctx.drawImage(canvasImage, 0, 0, finalImageWidth, finalImageHeight, x, y1, finalImageWidth, finalImageHeight); } } else if (repeat === "repeatX") { for (let x1 = x; x1 < x + width; x1 += imageWidth) { const finalImageWidth = Math.min(x + width - x1, imageWidth); const finalImageHeight = Math.min(y + height - y, imageHeight); ctx.drawImage(canvasImage, 0, 0, finalImageWidth, finalImageHeight, x1, y, finalImageWidth, finalImageHeight); } } } } exports.drawImages = drawImages; /** @internal */ function drawTexts(ctx, options) { const { measuredParagraph, width, height, fontFamily, fontStyle, fontWeight, fontSize, fontColor, strokeSize, strokeColor, valign, align, marginLeft, marginTop, marginRight, marginBottom, chopOverflow, useGlyphPadding, underlineSize, underlineColor, } = options; // return immediately if nothing to draw if (!measuredParagraph.measuredLines.length) { return; } const textCanvasWidth = useGlyphPadding ? measuredParagraph.boundingWidth : measuredParagraph.width; const textCanvasHeight = useGlyphPadding ? measuredParagraph.boundingHeight : measuredParagraph.height; // we add extra height (just fontSize) to make sure we able to draw characters with boxDescent const renderMargin = Math.round(fontSize / 2); const textCanvas = (0, canvas_1.createCanvas)(textCanvasWidth + renderMargin * 2, textCanvasHeight + renderMargin * 2); const parsedFontColor = (0, index_1.parseColorString)(fontColor); const parsedUnderlineStyle = (0, index_1.parseColorString)(underlineColor); const textCtx = textCanvas.getContext("2d"); textCtx.font = (0, index_1.getFontString)({ fontStyle, fontWeight, fontSize, fontFamily }); // we draw everything cuz align may need to display different parts (this can be optimized a bit if necessary) let y = useGlyphPadding ? fontSize + measuredParagraph.measuredLines[0].paddingTop : fontSize; for (const measuredLine of measuredParagraph.measuredLines) { let x = 0; if (align === "center") { x = (textCanvasWidth - measuredLine.width) / 2; if (useGlyphPadding) { x = (textCanvasWidth - (measuredLine.width - measuredLine.paddingLeft + measuredLine.paddingRight)) / 2; } } else if (align === "right") { x = textCanvasWidth - measuredLine.width; if (useGlyphPadding) { x = textCanvasWidth - measuredLine.width - measuredParagraph.paddingRight; } } else { if (useGlyphPadding) { x = measuredParagraph.paddingLeft; } } // draw underline if (underlineSize) { textCtx.fillStyle = parsedUnderlineStyle; textCtx.fillRect(x + renderMargin, y + renderMargin, measuredLine.width, underlineSize); } // draw the text textCtx.fillStyle = parsedFontColor; textCtx.fillText(measuredLine.text, x + renderMargin, y + renderMargin); // draw the stoke if have if (strokeSize) { textCtx.strokeStyle = (0, index_1.parseColorString)(strokeColor); textCtx.lineWidth = strokeSize; textCtx.strokeText(measuredLine.text, x + renderMargin, y + renderMargin); } // advance y y += (measuredLine.nextLineHeight); } // // if we want more precise spacing let sx = 0; let sy = 0; let tx = 0; let ty = 0; let sWidth = width; let sHeight = height; // measure what to display for align if (align === "right") { sx = textCanvasWidth - width + marginRight; if (chopOverflow) { tx += marginLeft; sx += marginLeft; sWidth = width - marginLeft - marginRight; } } else if (align === "center") { sx = (textCanvasWidth - width - marginLeft + marginRight) / 2; if (chopOverflow) { tx += marginLeft; sx += marginLeft; sWidth = width - marginLeft - marginRight; } } else { // left sx = -marginLeft; if (chopOverflow) { sWidth = width - marginRight; } } // measure what to display for valign if (valign === "bottom") { sy = textCanvasHeight - height + marginBottom; if (chopOverflow) { ty += marginTop; sy += marginTop; sHeight = height - marginTop - marginBottom; } } else if (valign === "middle") { sy = (textCanvasHeight - height - marginTop + marginBottom) / 2; if (chopOverflow) { ty += marginTop; sy += marginTop; sHeight = height - marginTop - marginBottom; } } else { // top sy = -marginTop; if (chopOverflow) { sHeight = height - marginBottom; } } // draw the image ctx.drawImage(textCanvas, sx + renderMargin, sy + renderMargin, sWidth, sHeight, tx, ty, sWidth, sHeight); } exports.drawTexts = drawTexts;