From c081f73780e80156435b6d3c67c17b5d39eb5477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E9=95=BF=E5=90=9B?= <352186537@qq.com> Date: Wed, 15 Apr 2026 10:09:29 +0800 Subject: [PATCH 1/2] Update URL persistence and text alignment --- README.md | 2 ++ nodes/artistic_text_preview.py | 36 ++++++++++++++++++----- nodes/image_loader.py | 53 ++++++++++++++++++++-------------- 3 files changed, 63 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index f367cc7..94b53fe 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ Workflow-compatible aliases: - `source_type = local`: select a file from `ComfyUI/input` - `source_type = url`: download from an HTTP/HTTPS URL +- Downloaded URL files are persisted under `ComfyUI/input/meux_downloads` when `persist_to_input = true` - Outputs `IMAGE` and the derived mask from alpha or grayscale when present #### MeuxMaskLoader @@ -336,6 +337,7 @@ Baidu Meux ComfyTools 是一组用于 ComfyUI 的自定义节点,服务于百 - `source_type = local`:从 `ComfyUI/input` 选择本地文件 - `source_type = url`:运行时下载 HTTP/HTTPS 图片 +- `persist_to_input = true` 时,URL 下载文件会保存到 `ComfyUI/input/meux_downloads` - 输出 `IMAGE`,以及图片自带 alpha/灰度派生的 `MASK` #### MeuxMaskLoader diff --git a/nodes/artistic_text_preview.py b/nodes/artistic_text_preview.py index 0f14588..a81a434 100644 --- a/nodes/artistic_text_preview.py +++ b/nodes/artistic_text_preview.py @@ -41,6 +41,8 @@ class MeuxArtisticTextPreview: "font_size": ("INT", {"default": 128, "min": 8, "max": 1024, "step": 1}), "char_spacing": ("INT", {"default": 0, "min": -20, "max": 200, "step": 1}), "line_spacing": ("INT", {"default": 0, "min": -512, "max": 512, "step": 1}), + "text_align": (["left", "center", "right"], {"default": "center"}), + "vertical_align": (["top", "middle", "bottom"], {"default": "middle"}), "bold": ("BOOLEAN", {"default": False}), "italic": ("BOOLEAN", {"default": False}), "background_color": ("STRING", {"default": "#000000"}), @@ -83,6 +85,8 @@ class MeuxArtisticTextPreview: font_size, char_spacing, line_spacing, + text_align, + vertical_align, bold, italic, background_color, @@ -132,6 +136,8 @@ class MeuxArtisticTextPreview: font=font, char_spacing=char_spacing, line_spacing=line_spacing, + text_align=text_align, + vertical_align=vertical_align, safe_area=safe_area, fake_bold=fake_bold, ) @@ -299,18 +305,17 @@ class MeuxArtisticTextPreview: return int(line_spacing) return max(6, font.size // 5 if hasattr(font, "size") else 6) - def _render_text_mask(self, text, width, height, font, char_spacing, line_spacing, safe_area, fake_bold): + def _render_text_mask( + self, text, width, height, font, char_spacing, line_spacing, text_align, vertical_align, safe_area, fake_bold + ): mask_image = Image.new("L", (width, height), 0) draw = ImageDraw.Draw(mask_image) line_spacing = self._resolve_line_spacing(font, line_spacing) - block_width, block_height = self._text_block_size(font, text, char_spacing, line_spacing) + _, block_height = self._text_block_size(font, text, char_spacing, line_spacing) left, top, right, bottom = safe_area - safe_width = right - left - safe_height = bottom - top - start_x = left + (safe_width - block_width) / 2 - start_y = top + (safe_height - block_height) / 2 + start_y = self._block_start_y(block_height, vertical_align, top, bottom) offsets = ((0, 0), (1, 0), (0, 1), (1, 1)) if fake_bold else ((0, 0),) lines = text.splitlines() or [text] @@ -319,7 +324,8 @@ class MeuxArtisticTextPreview: current_y = start_y for line in lines: - current_x = start_x + line_width = self._line_width(font, line, char_spacing) + current_x = self._line_start_x(line_width, text_align, left, right) for index, char in enumerate(line): for dx, dy in offsets: draw.text((current_x + dx, current_y + dy), char, fill=255, font=font) @@ -330,6 +336,22 @@ class MeuxArtisticTextPreview: return mask_image + def _line_start_x(self, line_width, text_align, left, right): + safe_width = right - left + if text_align == "left": + return left + if text_align == "right": + return right - line_width + return left + (safe_width - line_width) / 2 + + def _block_start_y(self, block_height, vertical_align, top, bottom): + safe_height = bottom - top + if vertical_align == "top": + return top + if vertical_align == "bottom": + return bottom - block_height + return top + (safe_height - block_height) / 2 + def _apply_fake_italic(self, mask_image, shear=0.25): bbox = mask_image.getbbox() if bbox is None: diff --git a/nodes/image_loader.py b/nodes/image_loader.py index dc4e383..c667901 100644 --- a/nodes/image_loader.py +++ b/nodes/image_loader.py @@ -17,6 +17,8 @@ class ImageLoader: Hybrid image loader that mirrors ComfyUI's Load Image node while adding URL download support. """ + URL_DOWNLOAD_SUBDIR = "meux_downloads" + RETURN_TYPES = ("IMAGE", "MASK") RETURN_NAMES = ("image", "mask") FUNCTION = "load_image" @@ -180,24 +182,21 @@ class ImageLoader: return mask def _persist_image(self, image: Image.Image, filename_hint: str, url: str, overwrite: bool): - try: - input_dir = folder_paths.get_input_directory() - except KeyError: - raise ValueError("未检测到 ComfyUI 的 input 目录,请在根目录创建 input 文件夹后重启。") - + input_dir = self._get_input_directory() + persist_dir = self._get_url_persist_directory(create=True) extension = self._infer_extension(image, url) - filename = self._build_filename(filename_hint or os.path.basename(urlparse(url).path), extension, url) - full_path = os.path.abspath(os.path.join(input_dir, filename)) - input_dir_abs = os.path.abspath(input_dir) - if not (full_path.startswith(input_dir_abs + os.sep) or full_path == input_dir_abs): - raise ValueError("无法写入到 input 目录之外。") + filename = self._build_cached_filename(filename_hint, extension, url) + full_path = os.path.abspath(os.path.join(persist_dir, filename)) + persist_dir_abs = os.path.abspath(persist_dir) + if not (full_path.startswith(persist_dir_abs + os.sep) or full_path == persist_dir_abs): + raise ValueError("无法写入到 URL 下载缓存目录之外。") if os.path.exists(full_path) and not overwrite: stem, ext = os.path.splitext(filename) counter = 1 while True: candidate = f"{stem}_{counter}{ext}" - candidate_path = os.path.join(input_dir, candidate) + candidate_path = os.path.join(persist_dir, candidate) if not os.path.exists(candidate_path): full_path = candidate_path break @@ -210,19 +209,31 @@ class ImageLoader: else: image.save(full_path) print(f"[MeuxImageLoader] 已保存到 {os.path.relpath(full_path, input_dir)}") + return full_path - def _build_filename(self, hint: str, extension: str, url: str) -> str: - sanitized = self._sanitize_filename(hint) if hint else "" - if sanitized.endswith(extension): - base = sanitized[: -len(extension)] - else: - base = sanitized or "" + def _build_cached_filename(self, filename_hint: str, extension: str, url: str) -> str: + digest = hashlib.sha1(url.encode("utf-8")).hexdigest()[:16] + path_name = os.path.basename(urlparse(url).path) + label_source = filename_hint or path_name or "" + label = self._sanitize_filename(label_source) + if label.endswith(extension): + label = label[: -len(extension)] + if label: + return f"meux_url_{digest}__{label}{extension}" + return f"meux_url_{digest}{extension}" - if not base: - digest = hashlib.sha1(url.encode("utf-8")).hexdigest()[:12] - base = f"meux_url_{digest}" + def _get_input_directory(self) -> str: + try: + return folder_paths.get_input_directory() + except KeyError: + raise ValueError("未检测到 ComfyUI 的 input 目录,请在根目录创建 input 文件夹后重启。") - return f"{base}{extension}" + def _get_url_persist_directory(self, create: bool) -> str: + input_dir = self._get_input_directory() + persist_dir = os.path.join(input_dir, self.URL_DOWNLOAD_SUBDIR) + if create: + os.makedirs(persist_dir, exist_ok=True) + return persist_dir def _infer_extension(self, image: Image.Image, url: str) -> str: parsed = urlparse(url) From 5e7a47bfe588da90315c7d40e9c0156118493b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E9=95=BF=E5=90=9B?= <352186537@qq.com> Date: Wed, 15 Apr 2026 10:34:47 +0800 Subject: [PATCH 2/2] Bump version to 1.7.1 --- README.md | 10 ++++++++-- __init__.py | 2 +- pyproject.toml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 94b53fe..c7f4f13 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ### Overview Baidu Meux ComfyTools is a collection of custom nodes for ComfyUI used by the Baidu Meux asset platform. -Current version: **1.7.0** +Current version: **1.7.1** Main nodes: @@ -38,6 +38,9 @@ Workflow-compatible aliases: ### Changelog +- **v1.7.1** + - Updated URL persistence so downloaded files are stored under `ComfyUI/input/meux_downloads`. + - Added `text_align` and `vertical_align` controls to `MeuxArtisticTextPreview`. - **v1.7.0** - Added `MeuxArtisticTextPreview` for transparent text preview rendering with fill, outline, spacing, mask output, and preset-based effects. - Added a project-level `fonts/` directory and made it the first font lookup location for the preview node. @@ -253,7 +256,7 @@ Baidu_Meux_ComfyTools/ ### 概述 Baidu Meux ComfyTools 是一组用于 ComfyUI 的自定义节点,服务于百度 Meux 资产平台相关工作流。 -当前版本:**1.7.0** +当前版本:**1.7.1** 主要节点: @@ -282,6 +285,9 @@ Baidu Meux ComfyTools 是一组用于 ComfyUI 的自定义节点,服务于百 ### 更新日志 +- **v1.7.1** + - 调整 URL 落盘位置,下载文件现在保存到 `ComfyUI/input/meux_downloads` + - 为 `MeuxArtisticTextPreview` 新增 `text_align` 和 `vertical_align` 对齐控制 - **v1.7.0** - 新增 `MeuxArtisticTextPreview`,用于透明背景文字预览,支持填充、描边、字距、遮罩输出和预设效果。 - 新增项目级 `fonts/` 目录,并作为预览节点的第一字体读取位置。 diff --git a/__init__.py b/__init__.py index 9c591dd..8df6096 100644 --- a/__init__.py +++ b/__init__.py @@ -62,7 +62,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { } # 可选:添加版本和作者信息 -__version__ = "1.7.0" +__version__ = "1.7.1" __author__ = "fangchangjun" # 调试信息 - 可以帮助确认导入是否成功 diff --git a/pyproject.toml b/pyproject.toml index 3137354..a149f34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "baidu_meux_comfy_tools" description = "Baidu Meux ComfyTools is a collection of custom nodes that streamline common workflow chores in ComfyUI for the Baidu Meux asset platform.." -version = "1.7.0" +version = "1.7.1" license = {file = "LICENSE"} # classifiers = [ # # For OS-independent nodes (works on all operating systems)