From b2bcb6616cf15dad82f78193b9fc27326f033de0 Mon Sep 17 00:00:00 2001 From: cyborvirtue <2088953655@qq.com> Date: Thu, 22 May 2025 10:50:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=A0=B9=E6=8D=AEjson?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E8=BF=9B=E8=A1=8C=E5=9B=BE=E5=B1=82=E5=8F=A0?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__pycache__/compose_poster.cpython-38.pyc | Bin 2159 -> 0 bytes scripts/__pycache__/export_psd.cpython-38.pyc | Bin 2352 -> 0 bytes .../__pycache__/export_psd2.cpython-38.pyc | Bin 1954 -> 0 bytes scripts/export_psd_from_json.py | 197 ++++++++++++++++++ 4 files changed, 197 insertions(+) delete mode 100644 scripts/__pycache__/compose_poster.cpython-38.pyc delete mode 100644 scripts/__pycache__/export_psd.cpython-38.pyc delete mode 100644 scripts/__pycache__/export_psd2.cpython-38.pyc create mode 100644 scripts/export_psd_from_json.py diff --git a/scripts/__pycache__/compose_poster.cpython-38.pyc b/scripts/__pycache__/compose_poster.cpython-38.pyc deleted file mode 100644 index 2326f4d6626111ddc648977d71478a0b5dd39b71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2159 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 diff --git a/scripts/__pycache__/export_psd2.cpython-38.pyc b/scripts/__pycache__/export_psd2.cpython-38.pyc deleted file mode 100644 index 31e06f01c31446bbdd5d260a011dfb3c6728728e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1954 zcmYinOK%%RcxE5=dUtK7PWnheXaFHOFMU!57} zVXe883qqB8KtiG*5`+Z3a4vskt`vp-0K@?fd^2`THkt1+^S!@s#`o)W7s2<=-JJP>M^@x~XGgWj{lX6Fk#!ff{a4@(wXt+I?%ueShFcV9`-Je; z^0Z)-^El6Lw@qoS$6QGJeg?d>?~RItJ~!e&O@v|sK$>?x{;+LJFII+nMJR?`dih8c zBcVCz^}}p8v1* z8BYw1p5pO_un#MxSsF*i#1hpKJ;pcCXY5mBVoxfZPG`AOyDig1?4$qX{ z5%lWO`#`r;bQp=n$SIYc@mO&x`XzOuB8Y0|(ZnyU?X!@B`vgzwWgYbVnVwhrb3Gek z2dzm1?D=J5zP}jN#ksQa_1=o%+C_*<;9Fxf6qJMnW{V8sgpvWvQ{WRABa#n@o)f-8xCr|@akfRokfu|i&0GkO z6*>GDt22W$be(PS?tF_(%}mFwOVo%e+j{!6{*cZbol-e9lwGm`gG%;>lu;$m!(G6j zG>>|kjmqfM_;$%%7-Hq=M0R>eLQXF2#gQ1UkRgq?hN6ASuNwI-S<9205k;TY*A<%j zx~Fa=-G@|q`3{4MqF06jiW*ne!vo65*d&+9_|18geAMs5RUM3yb=_3*fGWL6L zhj(MTw_0RdZ5-Hxob~DW%*J$ACp?vG&W**{An%fK>-H$!1YeM4qMSn6#|EtW3Mq0P z3wQ^*&#b`Un;25{dW6%QqQ=6m=M?u_uPR>;R zS(s8;4L7;kVJc&3W^_;1Xf{eIgJELu%phsy1%(GlS2?Imt1O+(kW*zLa5v(8n&>J8 zRW))&oG|si1m>Ofo3auXpdJO5>a?^A7H2{_yhvg}_eD_agMvzqN;3gBmX69w=s-vl zhEdjT?)Pb-a&Ip%^{6vX0bhZ-_EPXj;y7^PbW4wAVCzX|Zz++tm58g$+0)_LnhyL~ zm!=xcc@#aZ8`M;^QBRwu&k=hU%>4BcfX;mmjp^)!Ntni+KYsf8<%=(0zWAzheE9A0 z*FU^E`tA7HubnXNfz)o?rybsBaUpnz?iV?OcbP|5R*M69ZaOlG@2Q$i-E#G_#8&~J zg*Ob#@XZCoHCwo4zHXkwzUktIxrBYJFf81{3;3$h!mJG%ZG$<082sF!b|a`M(<09k zu3HthEm*$AV54P3zn1aDyDpZ@@!YaLer diff --git a/scripts/export_psd_from_json.py b/scripts/export_psd_from_json.py new file mode 100644 index 0000000..3796568 --- /dev/null +++ b/scripts/export_psd_from_json.py @@ -0,0 +1,197 @@ + + +""" +测试文件:从JSON配置文件创建PSD文件 +支持通过配置文件精确控制图层位置和属性 +""" + +import json +from psd_tools import PSDImage +from PIL import Image +from psd_tools.constants import Compression +import os +from typing import List, Tuple + +# 导入PixelLayer类,用于从PIL图像创建图层 +from psd_tools.api.layers import PixelLayer + + +def create_psd_from_config(config_file: str) -> None: + """ + 从JSON配置文件创建PSD文件 + + 参数: + config_file: JSON配置文件路径 + """ + # 确保配置文件存在 + if not os.path.exists(config_file): + raise FileNotFoundError(f"配置文件不存在: {config_file}") + + # 读取JSON配置文件 + with open(config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + + try: + # 1. 从配置创建PSD文件 + canvas = config['canvas'] + psd = PSDImage.new( + canvas['mode'], + (canvas['width'], canvas['height']) + ) + + # 2. 根据配置添加图层 + for layer_config in config['layers']: + # 打开图片 + image = Image.open(layer_config['image_path']) + + # 获取位置信息 + position = layer_config['position'] + left = position['left'] + top = position['top'] + + # 创建图层 + layer = PixelLayer.frompil( + image, + psd, + layer_config['name'], + top, + left, + Compression.RLE + ) + + # 设置图层可见性 + layer.visible = layer_config.get('visible', True) + psd.append(layer) + + # 3. 确保所有图层都是可见的 + for layer in psd: + if not layer.visible: + print(f"图层 {layer.name} 不可见,正在设置为可见") + layer.visible = True + + # 4. 生成合成图像并更新PSD文件的图像数据 + composite_image = psd.composite(force=True) + psd._record.image_data.set_data( + [channel.tobytes() for channel in composite_image.split()], + psd._record.header + ) + + # 5. 保存PSD文件 + output_config = config['output'] + output_path = output_config['path'] + + # 确保输出目录存在 + os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) + + psd.save(output_path) + print(f"PSD文件已成功创建,保存在: {output_path}") + + # 6. 生成预览(如果配置中启用) + if output_config.get('generate_preview', False): + preview_path = os.path.splitext(output_path)[0] + "_预览.png" + composite_image.save(preview_path) + print(f"预览已保存在: {preview_path}") + + # 7. 验证PSD文件结构 + saved_psd = PSDImage.open(output_path) + print(f"PSD文件信息: {saved_psd}") + print(f"图层数量: {len(saved_psd)}") + for i, layer in enumerate(saved_psd): + print(f"图层 {i}: {layer.name}, 位置: ({layer.left}, {layer.top}), 大小: {layer.width}x{layer.height}") + + except Exception as e: + print(f"创建PSD文件时出错: {e}") + + +def create_psd_from_images( + image_paths: List[str], + output_path: str, + canvas_size: Tuple[int, int] = (1000, 800), + mode: str = 'RGB' +) -> None: + """ + 从图片列表创建PSD文件,将图片从底到顶堆叠(保留原有功能) + + 参数: + image_paths: 图片路径列表 + output_path: 保存PSD文件的路径 + canvas_size: PSD画布大小,格式为(宽度, 高度) + mode: PSD文件的颜色模式 + """ + # 确保输出目录存在 + os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) + + try: + # 1. 创建一个新的PSD文件 + psd = PSDImage.new(mode, canvas_size) + + # 2. 打开并添加每个图片作为图层 + for i, img_path in enumerate(image_paths): + # 打开图片 + image = Image.open(img_path) + + # 计算居中位置 + left = (canvas_size[0] - image.width) // 2 + top = (canvas_size[1] - image.height) // 2 + + # 根据图片文件名创建图层名称 + layer_name = f"layer {i+1} - {os.path.basename(img_path)}" + + # 创建并添加图层 + layer = PixelLayer.frompil(image, psd, layer_name, top, left, Compression.RLE) + # 确保图层可见 + layer.visible = True + psd.append(layer) + + # 确保所有图层都是可见的 + for layer in psd: + if not layer.visible: + print(f"图层 {layer.name} 不可见,正在设置为可见") + layer.visible = True + + # 生成合成图像 + composite_image = psd.composite(force=True) + + # 更新PSD文件的图像数据 + psd._record.image_data.set_data([channel.tobytes() for channel in composite_image.split()], psd._record.header) + + # 3. 保存PSD文件 + psd.save(output_path) + print(f"PSD文件已成功创建,保存在: {output_path}") + + # 4. 生成并保存预览 + preview_path = os.path.splitext(output_path)[0] + "_预览.png" + composite_image.save(preview_path) + print(f"预览已保存在: {preview_path}") + + # 5. 验证PSD文件结构 + saved_psd = PSDImage.open(output_path) + print(f"PSD文件信息: {saved_psd}") + print(f"图层数量: {len(saved_psd)}") + for i, layer in enumerate(saved_psd): + print(f"图层 {i}: {layer.name}, 位置: ({layer.left}, {layer.top}), 大小: {layer.width}x{layer.height}") + + except Exception as e: + print(f"创建PSD文件时出错: {e}") + + +if __name__ == "__main__": + # 方法1: 使用JSON配置文件 + print("=== 使用JSON配置文件创建PSD ===") + create_psd_from_config('../configs/example.json') + + print("\n" + "="*50 + "\n") + + # 方法2: 使用原有的图片列表方法(保留兼容性) + print("=== 使用图片列表创建PSD(居中布局)===") + image_list = [ + '../images/background.jpg', # 底层图片 + '../images/nankai.jpg', # 中间图片 + '../images/aaai.png', # 顶层图片 + # 可以根据需要添加更多图片 + ] + + create_psd_from_images( + image_paths=image_list, + output_path='../outputs/combined_output.psd' + ) \ No newline at end of file