diff --git a/README.MD b/README.MD
index b02c434..05b6583 100644
--- a/README.MD
+++ b/README.MD
@@ -37,6 +37,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
## Update
**If the dependency package error after updating, please reinstall the relevant dependency packages. for details, please refer to [here](https://github.com/chflame163/ComfyUI_LayerStyle/issues/5).
+* Commit [SimpleTextImage](#SimpleTextImage) node, it generate simple typesetting images and masks from text. This node references some of the functionalities and code of [ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite](https://github.com/ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite).
* Commit [PromptTagger](#PromptTagger) node,Inference the prompts based on the image. and it can replace key word for the prompt(need apply for Google Studio API key). Upgrade [ColorImageV2](#ColorImageV2) and [GradientImageV2](#GradientImageV2),support user customize preset sizes and size_as input.
* Commit [LaMa](#LaMa) node, it can erase objects from the image based on the mask. this node is repackage of [IOPaint](https://www.iopaint.com).
* Commit [ImageRemoveAlpha](#ImageRemoveAlpha) and [ImageCombineAlpha](#ImageCombineAlpha) nodes, alpha channel of the image can be removed or merged.
@@ -652,6 +653,33 @@ The following changes have been made based on GradientImage:
*Only limited to input images and masks. forcing the integration of other types of inputs will result in node errors.
**The preset size is defined in ```custom_size.ini```, which is located in the root directory of the plugin. Open with text editing software. Each row represents a size, with the first value being width and the second being height, separated by a lowercase "x" in the middle. To avoid errors, please do not enter extra characters.
+### SimpleTextImage
+
+Generate simple typesetting images and masks from text. This node references some of the functionalities and code of [ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite](https://github.com/ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite).
+
+
+Node options:
+* size_as*: The input image or mask here will generate the output image and mask according to their size. this input takes priority over the width and height below.
+* font_file**: Here is a list of available font files in the font folder, and the selected font files will be used to generate images.
+* align: Alignment options. There are three options: center, left, and right.
+* char_per_line: The number of characters per line, any excess will be automatically wrapped.
+* leading: The leading space.
+* font_size: The size of font.
+* text_color: The color of text.
+* stroke_width: The width of stroke.
+* stroke_color: The color of stroke.
+* x_offset: The horizontal offset of the text position.
+* y_offset: The vertical offset of the text position.
+* width: Width of the image. If there is a size_as input, this setting will be ignored.
+* height: Height of the image. If there is a size_as input, this setting will be ignored.
+
+
+*Only limited to input image and mask. forcing the integration of other types of inputs will result in node errors.
+
+**The font folder is defined in ```resource_dir.ini```, this file is located in the root directory of the plugin.
+Open the text editing software and find the line starting with "FONT_dir=", after "=", enter the custom folder path name. all font files in this folder will be collected and displayed in the node list during ComfyUI initialization.
+If the folder set in ini is invalid, the font folder that comes with the plugin will be enabled.
+
### TextImage
diff --git a/README_CN.MD b/README_CN.MD
index 0d9ff14..11f84a3 100644
--- a/README_CN.MD
+++ b/README_CN.MD
@@ -38,6 +38,7 @@ git clone https://github.com/chflame163/ComfyUI_LayerStyle.git
## 更新说明
**如果本插件更新后出现依赖包错误,请重新安装相关依赖包。详情见[这里](https://github.com/chflame163/ComfyUI_LayerStyle/issues/5)。
+* 添加 [SimpleTextImage](#SimpleTextImage) 节点。从文字生成简单排版的图片以及遮罩。这个节点参考了[ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite](https://github.com/ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite)的部分功能和代码。
* 添加 [PromptTagger](#PromptTagger) 节点,根据图片反推提示词,可以替换关键词。需要申请Google Studio API使用。升级节点[ColorImageV2](#ColorImageV2)和[GradientImageV2](#GradientImageV2),支持用户自定义预设尺寸和size_as输入。
* 添加 [Lama](#Lama) 节点,根据图像遮罩擦除物体。本节点是对[IOPaint](https://www.iopaint.com)的封装。
* 添加 [ImageRemoveAlpha](#ImageRemoveAlpha) 和 [ImageCombineAlpha](#ImageCombineAlpha) 节点,可移除或合并图片的alpha通道。
@@ -648,6 +649,36 @@ GradientImage的V2升级版。
**预设尺寸在```custom_size.ini```中定义,这个文件位于插件根目录下。用文本编辑软件打开,编辑自定义尺寸。每行表示一个尺寸,第一个数值是宽度,第二个是高度,中间用小写的"x"分隔。为避免错误请不要输入多余的字符。
+
+
+### SimpleTextImage
+
+从文字生成简单排版的图片以及遮罩。这个节点参考了[ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite](https://github.com/ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite)的部分功能和代码。
+
+
+节点选项说明:
+* size_as*: 此处输入图像或遮罩,将按照其尺寸生成输出图像和遮罩。注意,此输入优先级高于下面的width和height。
+* text: 文字输入。
+* font_file**: 这里列出了font文件夹中可用的字体文件列表,选中的字体文件将被用来生成图像。
+* align: 对齐选项。有居中,靠左和靠右三个选项。
+* char_per_line: 每行字符数量,超过的部分将自动换行。
+* leading: 行间距。
+* font_size: 字体大小。
+* text_color: 文字颜色。
+* stroke_width: 描边宽度。
+* stroke_color: 描边颜色。
+* x_offset: 文字位置的水平偏移量。
+* y_offset: 文字位置的垂直偏移量。
+* width: 画面的宽度。如果有size_as输入,此设置将被忽略。
+* height: 画面的高度。如果有size_as输入,此设置将被忽略。
+
+
+*仅限输入image和mask, 如果强制接入其他类型输入,将导致节点错误。
+**font文件夹在```resource_dir.ini```中定义,这个文件位于插件根目录下。用文本编辑软件打开,找到“FONT_dir=”开头的这一行,编辑“=”之后为自定义文件夹路径名。这个文件夹里面所有的.ttf和.otf文件将在ComfyUI初始化时被收集并显示在节点的列表中。如果ini中设定的文件夹无效,将启用插件自带的font文件夹。
+
+
+
+
### TextImage

从文字生成图片以及遮罩。支持字间距行间距调整,横排竖排调整,可设置文字的随机变化,包括大小和位置的随机变化。
@@ -670,7 +701,7 @@ GradientImage的V2升级版。
* background_color: 背景颜色。
-
+*仅限输入image和mask, 如果强制接入其他类型输入,将导致节点错误。
**font文件夹在```resource_dir.ini```中定义,这个文件位于插件根目录下。用文本编辑软件打开,找到“FONT_dir=”开头的这一行,编辑“=”之后为自定义文件夹路径名。这个文件夹里面所有的.ttf和.otf文件将在ComfyUI初始化时被收集并显示在节点的列表中。如果ini中设定的文件夹无效,将启用插件自带的font文件夹。
diff --git a/image/simple_text_image_example.png b/image/simple_text_image_example.png
new file mode 100644
index 0000000..e253c6d
Binary files /dev/null and b/image/simple_text_image_example.png differ
diff --git a/image/simple_text_image_node.png b/image/simple_text_image_node.png
new file mode 100644
index 0000000..7935bf3
Binary files /dev/null and b/image/simple_text_image_node.png differ
diff --git a/py/imagefunc.py b/py/imagefunc.py
index 44c7e13..b82b34a 100644
--- a/py/imagefunc.py
+++ b/py/imagefunc.py
@@ -16,6 +16,7 @@ import scipy.ndimage
import cv2
import random
import time
+from functools import lru_cache
from typing import Union, List
from PIL import Image, ImageFilter, ImageChops, ImageDraw, ImageOps, ImageEnhance, ImageFont
from skimage import img_as_float, img_as_ubyte
@@ -894,7 +895,7 @@ def generate_text_image(text:str, font_path:str, font_size:int, text_color:str="
'''Mask Functions'''
-
+@lru_cache(maxsize=1, typed=False)
def load_RMBG_model():
current_directory = os.path.dirname(os.path.abspath(__file__))
device = "cuda" if torch.cuda.is_available() else "cpu"
diff --git a/py/simple_text_image.py b/py/simple_text_image.py
new file mode 100644
index 0000000..3dccff9
--- /dev/null
+++ b/py/simple_text_image.py
@@ -0,0 +1,103 @@
+from .imagefunc import *
+import textwrap
+from typing import cast
+
+NODE_NAME = 'SimpleTextImage'
+any = AnyType("*")
+
+class SimpleTextImage:
+
+ def __init__(self):
+ pass
+
+ @classmethod
+ def INPUT_TYPES(self):
+
+ return {
+ "required": {
+ "text": ("STRING",{"default": "text", "multiline": True},
+ ),
+ "font_file": (FONT_LIST,),
+ "align": (["center", "left", "right"],),
+ "char_per_line": ("INT", {"default": 80, "min": 1, "max": 8096, "step": 1},),
+ "leading": ("INT",{"default": 8, "min": 0, "max": 8096, "step": 1},),
+ "font_size": ("INT",{"default": 72, "min": 1, "max": 2500, "step": 1},),
+ "text_color": ("STRING", {"default": "#FFFFFF"},),
+ "stroke_width": ("INT",{"default": 0, "min": 0, "max": 8096, "step": 1},),
+ "stroke_color": ("STRING",{"default": "#FF8000"},),
+ "x_offset": ("INT", {"default": 0, "min": 0, "max": 8096, "step": 1},),
+ "y_offset": ("INT", {"default": 0, "min": 0, "max": 8096, "step": 1},),
+ "width": ("INT", {"default": 512, "min": 1, "max": 8096, "step": 1},),
+ "height": ("INT", {"default": 512, "min": 1, "max": 8096, "step": 1},),
+ },
+ "optional": {
+ "size_as": (any, {}),
+ }
+ }
+
+ RETURN_TYPES = ("IMAGE", "MASK",)
+ RETURN_NAMES = ("image", "mask",)
+ FUNCTION = 'simple_text_image'
+ CATEGORY = '😺dzNodes/LayerUtility'
+ OUTPUT_NODE = True
+
+ def simple_text_image(self, text, font_file, align, char_per_line,
+ leading, font_size, text_color,
+ stroke_width, stroke_color, x_offset, y_offset,
+ width, height, size_as=None
+ ):
+
+ if size_as is not None:
+ width, height = tensor2pil(size_as).size
+ font_path = FONT_DICT.get(font_file)
+ (_, top, _, _) = ImageFont.truetype(font=font_path, size=font_size, encoding='unic').getbbox(text)
+ font = cast(ImageFont.FreeTypeFont, ImageFont.truetype(font_path, font_size))
+ if char_per_line == 0:
+ char_per_line = int(width / font_size)
+ paragraphs = text.split('\n')
+
+ img_height = height # line_height * len(lines)
+ img_width = width # max(font.getsize(line)[0] for line in lines)
+
+ img = Image.new("RGBA", size=(img_width, img_height), color=(0, 0, 0, 0))
+ draw = ImageDraw.Draw(img)
+
+ y_text = y_offset + stroke_width
+
+ for paragraph in paragraphs:
+ lines = textwrap.wrap(paragraph, width=char_per_line, expand_tabs=False, replace_whitespace=False)
+ for line in lines:
+ width = font.getbbox(line)[2] - font.getbbox(line)[0]
+ height = font.getbbox(line)[3] - font.getbbox(line)[1]
+
+ # 根据 align 参数重新计算 x 坐标
+ if align == "left":
+ x_text = x_offset
+ elif align == "center":
+ x_text = (img_width - width) // 2
+ elif align == "right":
+ x_text = img_width - width - margin_x
+ else:
+ x_text = x_offset # 默认为左对齐
+
+ draw.text(
+ xy=(x_text, y_text),
+ text=line,
+ fill=text_color,
+ font=font,
+ stroke_width=stroke_width,
+ stroke_fill=stroke_color,
+ )
+ y_text += height + leading
+
+ y_text += leading * 2
+
+ return (pil2tensor(img), image2mask(img.split()[3]))
+
+NODE_CLASS_MAPPINGS = {
+ "LayerUtility: SimpleTextImage": SimpleTextImage
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "LayerUtility: SimpleTextImage": "LayerUtility: SimpleTextImage"
+}
\ No newline at end of file
diff --git a/workflow/simple_text_example.json b/workflow/simple_text_example.json
new file mode 100644
index 0000000..b662baa
--- /dev/null
+++ b/workflow/simple_text_example.json
@@ -0,0 +1,360 @@
+{
+ "last_node_id": 11,
+ "last_link_id": 23,
+ "nodes": [
+ {
+ "id": 7,
+ "type": "LayerStyle: DropShadow",
+ "pos": [
+ 1056,
+ 302
+ ],
+ "size": {
+ "0": 315,
+ "1": 266
+ },
+ "flags": {},
+ "order": 2,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "background_image",
+ "type": "IMAGE",
+ "link": 10
+ },
+ {
+ "name": "layer_image",
+ "type": "IMAGE",
+ "link": 18
+ },
+ {
+ "name": "layer_mask",
+ "type": "MASK",
+ "link": null
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 19,
+ 23
+ ],
+ "shape": 3,
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerStyle: DropShadow"
+ },
+ "widgets_values": [
+ true,
+ "normal",
+ 75,
+ 8,
+ 8,
+ 2,
+ 8,
+ "#000000"
+ ]
+ },
+ {
+ "id": 4,
+ "type": "LoadImage",
+ "pos": [
+ 523,
+ 289
+ ],
+ "size": {
+ "0": 315,
+ "1": 314
+ },
+ "flags": {},
+ "order": 0,
+ "mode": 0,
+ "outputs": [
+ {
+ "name": "IMAGE",
+ "type": "IMAGE",
+ "links": [
+ 10,
+ 17
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "MASK",
+ "type": "MASK",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LoadImage"
+ },
+ "widgets_values": [
+ "1344x768_beach (26).png",
+ "image"
+ ]
+ },
+ {
+ "id": 9,
+ "type": "LayerUtility: SimpleTextImage",
+ "pos": [
+ 741,
+ 667
+ ],
+ "size": [
+ 392.5400561523438,
+ 406.28336669921873
+ ],
+ "flags": {},
+ "order": 1,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "size_as",
+ "type": "*",
+ "link": 17
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 18
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerUtility: SimpleTextImage"
+ },
+ "widgets_values": [
+ "Are you going to Scarborough Fair?\nParsley, sage, rosemary, and thyme.\nRemember me to one who lives there,\nShe once was the true love of mine.",
+ "Alibaba-PuHuiTi-Bold.ttf",
+ "center",
+ 80,
+ 8,
+ 52,
+ "#FFFFFF",
+ 0,
+ "#FF8000",
+ 0,
+ 300,
+ 512,
+ 512
+ ]
+ },
+ {
+ "id": 10,
+ "type": "LayerUtility: SimpleTextImage",
+ "pos": [
+ 1310,
+ 666
+ ],
+ "size": {
+ "0": 392.5400695800781,
+ "1": 406.2833557128906
+ },
+ "flags": {},
+ "order": 3,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "size_as",
+ "type": "*",
+ "link": 23
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 21
+ ],
+ "shape": 3,
+ "slot_index": 0
+ },
+ {
+ "name": "mask",
+ "type": "MASK",
+ "links": null,
+ "shape": 3
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerUtility: SimpleTextImage"
+ },
+ "widgets_values": [
+ "Scarborough Fair",
+ "Alibaba-PuHuiTi-Bold.ttf",
+ "center",
+ 80,
+ 8,
+ 108,
+ "#FFE080",
+ 0,
+ "#FF8000",
+ 0,
+ 120,
+ 512,
+ 512
+ ]
+ },
+ {
+ "id": 11,
+ "type": "LayerStyle: DropShadow",
+ "pos": [
+ 1678,
+ 302
+ ],
+ "size": {
+ "0": 315,
+ "1": 266
+ },
+ "flags": {},
+ "order": 4,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "background_image",
+ "type": "IMAGE",
+ "link": 19
+ },
+ {
+ "name": "layer_image",
+ "type": "IMAGE",
+ "link": 21
+ },
+ {
+ "name": "layer_mask",
+ "type": "MASK",
+ "link": null
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 20
+ ],
+ "shape": 3,
+ "slot_index": 0
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "LayerStyle: DropShadow"
+ },
+ "widgets_values": [
+ true,
+ "normal",
+ 75,
+ 8,
+ 8,
+ 2,
+ 8,
+ "#000000"
+ ]
+ },
+ {
+ "id": 2,
+ "type": "PreviewImage",
+ "pos": [
+ 2056,
+ 355
+ ],
+ "size": [
+ 714.4351806640625,
+ 444.5991973876953
+ ],
+ "flags": {},
+ "order": 5,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 20
+ }
+ ],
+ "properties": {
+ "Node name for S&R": "PreviewImage"
+ }
+ }
+ ],
+ "links": [
+ [
+ 10,
+ 4,
+ 0,
+ 7,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 17,
+ 4,
+ 0,
+ 9,
+ 0,
+ "*"
+ ],
+ [
+ 18,
+ 9,
+ 0,
+ 7,
+ 1,
+ "IMAGE"
+ ],
+ [
+ 19,
+ 7,
+ 0,
+ 11,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 20,
+ 11,
+ 0,
+ 2,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 21,
+ 10,
+ 0,
+ 11,
+ 1,
+ "IMAGE"
+ ],
+ [
+ 23,
+ 7,
+ 0,
+ 10,
+ 0,
+ "*"
+ ]
+ ],
+ "groups": [],
+ "config": {},
+ "extra": {},
+ "version": 0.4
+}
\ No newline at end of file