feat: AI套图分层方案 + Gemini集成 - 4种图案类型处理 + 正片叠底 + 宽高比 + 模型选择
Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -1,56 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<ExtensionManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ExtensionBundleId="com.designer.adminpanel" ExtensionBundleVersion="1.0.0" Version="8.0">
|
|
||||||
<ExtensionList>
|
|
||||||
<Extension Id="com.designer.adminpanel" Version="1.0.0"/>
|
|
||||||
</ExtensionList>
|
|
||||||
|
|
||||||
<ExecutionEnvironment>
|
|
||||||
<HostList>
|
|
||||||
<!-- Photoshop CC 2017-2024+ -->
|
|
||||||
<Host Name="PHSP" Version="[18.0,99.9]"/>
|
|
||||||
<Host Name="PHXS" Version="[18.0,99.9]"/>
|
|
||||||
</HostList>
|
|
||||||
|
|
||||||
<LocaleList>
|
|
||||||
<Locale Code="All"/>
|
|
||||||
</LocaleList>
|
|
||||||
|
|
||||||
<RequiredRuntimeList>
|
|
||||||
<RequiredRuntime Name="CSXS" Version="8.0"/>
|
|
||||||
</RequiredRuntimeList>
|
|
||||||
</ExecutionEnvironment>
|
|
||||||
|
|
||||||
<DispatchInfoList>
|
|
||||||
<Extension Id="com.designer.adminpanel">
|
|
||||||
<DispatchInfo>
|
|
||||||
<Resources>
|
|
||||||
<MainPath>./index.html</MainPath>
|
|
||||||
<CEFCommandLine>
|
|
||||||
<Parameter>--enable-nodejs</Parameter>
|
|
||||||
<Parameter>--mixed-context</Parameter>
|
|
||||||
</CEFCommandLine>
|
|
||||||
</Resources>
|
|
||||||
|
|
||||||
<Lifecycle>
|
|
||||||
<AutoVisible>true</AutoVisible>
|
|
||||||
</Lifecycle>
|
|
||||||
|
|
||||||
<UI>
|
|
||||||
<Type>Panel</Type>
|
|
||||||
<Menu>Designer Admin Panel</Menu>
|
|
||||||
<Geometry>
|
|
||||||
<Size>
|
|
||||||
<Height>800</Height>
|
|
||||||
<Width>1200</Width>
|
|
||||||
</Size>
|
|
||||||
<MinSize>
|
|
||||||
<Height>400</Height>
|
|
||||||
<Width>600</Width>
|
|
||||||
</MinSize>
|
|
||||||
</Geometry>
|
|
||||||
</UI>
|
|
||||||
</DispatchInfo>
|
|
||||||
</Extension>
|
|
||||||
</DispatchInfoList>
|
|
||||||
</ExtensionManifest>
|
|
||||||
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Designer Admin Panel</title>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#frame {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: 0;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 加载动画 */
|
|
||||||
#loading {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
text-align: center;
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
|
||||||
border: 3px solid #f3f3f3;
|
|
||||||
border-top: 3px solid #3498db;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin: 0 auto 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
#frame.loaded ~ #loading {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- 加载指示器 -->
|
|
||||||
<div id="loading">
|
|
||||||
<div class="spinner"></div>
|
|
||||||
<p>加载中...</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 主页面 iframe -->
|
|
||||||
<iframe
|
|
||||||
id="frame"
|
|
||||||
src="https://app.aidg168.uk"
|
|
||||||
allow="clipboard-read; clipboard-write"
|
|
||||||
></iframe>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const frame = document.getElementById('frame');
|
|
||||||
const loading = document.getElementById('loading');
|
|
||||||
let loadTimeout;
|
|
||||||
|
|
||||||
// iframe 加载完成
|
|
||||||
frame.addEventListener('load', function() {
|
|
||||||
clearTimeout(loadTimeout);
|
|
||||||
this.classList.add('loaded');
|
|
||||||
console.log('✅ 页面加载成功');
|
|
||||||
});
|
|
||||||
|
|
||||||
// iframe 加载错误
|
|
||||||
frame.addEventListener('error', function(e) {
|
|
||||||
clearTimeout(loadTimeout);
|
|
||||||
loading.innerHTML = '<div style="color: red; padding: 20px; text-align: center;">' +
|
|
||||||
'<h3>❌ 加载失败</h3>' +
|
|
||||||
'<p>无法连接到:https://app.aidg168.uk</p>' +
|
|
||||||
'<p>请检查网络连接或更换 URL</p>' +
|
|
||||||
'<button onclick="location.reload()" style="padding: 10px 20px; cursor: pointer;">重试</button>' +
|
|
||||||
'</div>';
|
|
||||||
console.error('❌ iframe 加载错误:', e);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 20秒超时处理
|
|
||||||
loadTimeout = setTimeout(function() {
|
|
||||||
if (!frame.classList.contains('loaded')) {
|
|
||||||
loading.innerHTML = '<div style="color: orange; padding: 20px; text-align: center;">' +
|
|
||||||
'<h3>⏱️ 加载超时</h3>' +
|
|
||||||
'<p>网页加载时间过长(超过 20 秒)</p>' +
|
|
||||||
'<p>当前 URL: https://app.aidg168.uk</p>' +
|
|
||||||
'<button onclick="location.reload()" style="padding: 10px 20px; cursor: pointer; margin: 5px;">重试</button>' +
|
|
||||||
'<button onclick="testUrl()" style="padding: 10px 20px; cursor: pointer; margin: 5px;">测试连接</button>' +
|
|
||||||
'</div>';
|
|
||||||
console.warn('⏱️ iframe 加载超时');
|
|
||||||
}
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
// 测试 URL 是否可访问
|
|
||||||
function testUrl() {
|
|
||||||
loading.innerHTML = '<div style="padding: 20px; text-align: center;">' +
|
|
||||||
'<div class="spinner"></div>' +
|
|
||||||
'<p>正在测试连接...</p></div>';
|
|
||||||
|
|
||||||
fetch('https://app.aidg168.uk', { mode: 'no-cors' })
|
|
||||||
.then(() => {
|
|
||||||
alert('✅ 网站可以访问,正在重新加载...');
|
|
||||||
location.reload();
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
loading.innerHTML = '<div style="color: red; padding: 20px; text-align: center;">' +
|
|
||||||
'<h3>❌ 网站无法访问</h3>' +
|
|
||||||
'<p>错误: ' + err.message + '</p>' +
|
|
||||||
'<button onclick="location.reload()" style="padding: 10px 20px; cursor: pointer;">返回</button>' +
|
|
||||||
'</div>';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 与 iframe 通信(可选)
|
|
||||||
window.addEventListener("message", function(e) {
|
|
||||||
if (e.origin === "https://app.aidg168.uk") {
|
|
||||||
console.log("📨 收到消息:", e.data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function sendToIframe(data) {
|
|
||||||
frame.contentWindow.postMessage(data, "https://app.aidg168.uk");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调试信息
|
|
||||||
console.log('🚀 AdminPanel 已启动');
|
|
||||||
console.log('📍 加载 URL:', frame.src);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
@echo off
|
|
||||||
chcp 65001 >nul
|
|
||||||
echo ========================================
|
|
||||||
echo CEP 扩展调试模式检查工具
|
|
||||||
echo ========================================
|
|
||||||
echo.
|
|
||||||
|
|
||||||
echo [1] 检查当前 CEP 调试模式状态...
|
|
||||||
reg query "HKEY_CURRENT_USER\Software\Adobe\CSXS.8" /v PlayerDebugMode 2>nul
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo ❌ 未找到 CSXS.8 调试模式配置
|
|
||||||
) else (
|
|
||||||
echo ✅ CSXS.8 配置已存在
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
|
|
||||||
echo [2] 启用 CEP 调试模式...
|
|
||||||
reg add "HKEY_CURRENT_USER\Software\Adobe\CSXS.8" /v PlayerDebugMode /t REG_SZ /d 1 /f >nul
|
|
||||||
reg add "HKEY_CURRENT_USER\Software\Adobe\CSXS.9" /v PlayerDebugMode /t REG_SZ /d 1 /f >nul
|
|
||||||
reg add "HKEY_CURRENT_USER\Software\Adobe\CSXS.10" /v PlayerDebugMode /t REG_SZ /d 1 /f >nul
|
|
||||||
reg add "HKEY_CURRENT_USER\Software\Adobe\CSXS.11" /v PlayerDebugMode /t REG_SZ /d 1 /f >nul
|
|
||||||
|
|
||||||
echo ✅ CEP 调试模式已启用(CSXS 8/9/10/11)
|
|
||||||
echo.
|
|
||||||
|
|
||||||
echo [3] 检查插件安装位置...
|
|
||||||
set "EXT_DIR=%APPDATA%\Adobe\CEP\extensions\AdminPanel"
|
|
||||||
if exist "%EXT_DIR%\" (
|
|
||||||
echo ✅ 插件已安装到: %EXT_DIR%
|
|
||||||
dir "%EXT_DIR%" /B
|
|
||||||
) else (
|
|
||||||
echo ❌ 插件未安装,请运行安装命令
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
|
|
||||||
echo ========================================
|
|
||||||
echo 操作完成!
|
|
||||||
echo 请 [完全关闭 Photoshop] 后重新打开
|
|
||||||
echo ========================================
|
|
||||||
pause
|
|
||||||
|
|
||||||
223
AI改图-双图.py
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
双图合成处理 - 基于 multi_image_merge 方法
|
||||||
|
使用 Gemini 3 Pro Image Preview 模型进行双图创意合成
|
||||||
|
|
||||||
|
特点:
|
||||||
|
- 支持本地图片和网络图片 URL 两种输入方式
|
||||||
|
- 支持自定义提示词
|
||||||
|
- 创意合成、场景融合
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
1. 安装依赖:pip install openai requests
|
||||||
|
2. 在下方配置区填入您的 API Key
|
||||||
|
3. 修改文件底部的参数变量
|
||||||
|
4. 运行:python 双图合成处理.py
|
||||||
|
|
||||||
|
获取 API Key:https://api.laozhang.ai/token
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
from openai import OpenAI
|
||||||
|
|
||||||
|
# ========== 配置区(请填入您的 API Key)==========
|
||||||
|
API_KEY = "sk-KZ9TGgZAWnzY1T3c90B277F72e184c26A53f22440e08E86e" # 替换为您的 API Key
|
||||||
|
BASE_URL = "https://api.laozhang.ai/v1"
|
||||||
|
|
||||||
|
# 模型选择
|
||||||
|
MODEL = "gemini-3-pro-image-preview" # 最新版,支持更高质量
|
||||||
|
# MODEL = "gemini-2.5-flash-image" # 稳定版,价格更低($0.025 vs $0.05)
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
|
||||||
|
def extract_and_save_image(content: str, filename: str) -> bool:
|
||||||
|
"""从响应内容中提取 base64 图片并保存"""
|
||||||
|
# 匹配 markdown 格式的 base64 图片
|
||||||
|
match = re.search(r'!\[.*?\]\((data:image/\w+;base64,([^)]+))\)', content)
|
||||||
|
if match:
|
||||||
|
base64_data = match.group(2)
|
||||||
|
# 确保 base64 填充正确
|
||||||
|
padding = 4 - len(base64_data) % 4
|
||||||
|
if padding != 4:
|
||||||
|
base64_data += '=' * padding
|
||||||
|
image_data = base64.b64decode(base64_data)
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
f.write(image_data)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_url(path: str) -> bool:
|
||||||
|
"""判断路径是否为 URL"""
|
||||||
|
return path.startswith('http://') or path.startswith('https://')
|
||||||
|
|
||||||
|
|
||||||
|
def multi_image_merge(image1_path: str, image2_path: str, prompt: str, output_filename: str = None) -> bool:
|
||||||
|
"""
|
||||||
|
多图合成
|
||||||
|
|
||||||
|
参数:
|
||||||
|
image1_path: 第一张图片路径(本地文件路径或 URL)
|
||||||
|
image2_path: 第二张图片路径(本地文件路径或 URL)
|
||||||
|
prompt: 合成提示词
|
||||||
|
output_filename: 输出文件名(可选)
|
||||||
|
|
||||||
|
返回: 是否成功
|
||||||
|
"""
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("🎨 双图合成处理")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
|
||||||
|
|
||||||
|
# 构建 content 数组
|
||||||
|
content = [{"type": "text", "text": prompt}]
|
||||||
|
|
||||||
|
# 处理第一张图片
|
||||||
|
if is_url(image1_path):
|
||||||
|
# 网络图片 URL
|
||||||
|
content.append({"type": "image_url", "image_url": {"url": image1_path}})
|
||||||
|
print(f"🖼️ 图片1 (URL): {image1_path[:50]}...")
|
||||||
|
else:
|
||||||
|
# 本地图片 Base64
|
||||||
|
if not os.path.exists(image1_path):
|
||||||
|
print(f"❌ 错误:图片1不存在: {image1_path}")
|
||||||
|
return False
|
||||||
|
with open(image1_path, "rb") as f:
|
||||||
|
image1_b64 = base64.b64encode(f.read()).decode("utf-8")
|
||||||
|
content.append({
|
||||||
|
"type": "image_url",
|
||||||
|
"image_url": {
|
||||||
|
"url": f"data:image/jpeg;base64,{image1_b64}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
print(f"🖼️ 图片1 (本地): {image1_path}")
|
||||||
|
|
||||||
|
# 处理第二张图片
|
||||||
|
if is_url(image2_path):
|
||||||
|
# 网络图片 URL
|
||||||
|
content.append({"type": "image_url", "image_url": {"url": image2_path}})
|
||||||
|
print(f"🖼️ 图片2 (URL): {image2_path[:50]}...")
|
||||||
|
else:
|
||||||
|
# 本地图片 Base64
|
||||||
|
if not os.path.exists(image2_path):
|
||||||
|
print(f"❌ 错误:图片2不存在: {image2_path}")
|
||||||
|
return False
|
||||||
|
with open(image2_path, "rb") as f:
|
||||||
|
image2_b64 = base64.b64encode(f.read()).decode("utf-8")
|
||||||
|
content.append({
|
||||||
|
"type": "image_url",
|
||||||
|
"image_url": {
|
||||||
|
"url": f"data:image/jpeg;base64,{image2_b64}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
print(f"🖼️ 图片2 (本地): {image2_path}")
|
||||||
|
|
||||||
|
print(f"📝 提示词: {prompt}")
|
||||||
|
print("📡 发送请求...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = client.chat.completions.create(
|
||||||
|
model=MODEL,
|
||||||
|
messages=[
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": content
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
result_content = response.choices[0].message.content
|
||||||
|
|
||||||
|
# 确定输出文件名
|
||||||
|
if output_filename:
|
||||||
|
output_file = output_filename
|
||||||
|
else:
|
||||||
|
output_file = "result_multi_merge.png"
|
||||||
|
|
||||||
|
if extract_and_save_image(result_content, output_file):
|
||||||
|
print(f"✅ 成功!图片已保存: {output_file}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"⚠️ 未找到图片数据")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 错误: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("="*60)
|
||||||
|
print("🎨 双图合成处理")
|
||||||
|
print(f"模型: {MODEL}")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
# 检查 API Key
|
||||||
|
if API_KEY == "sk-YOUR_API_KEY" or not API_KEY:
|
||||||
|
print("\n❌ 请先配置您的 API Key!")
|
||||||
|
print(" 获取地址: https://api.laozhang.ai/token")
|
||||||
|
print(" 然后修改脚本顶部的 API_KEY 变量")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查图片是否存在(仅本地文件)
|
||||||
|
if not is_url(IMAGE1_PATH) and not os.path.exists(IMAGE1_PATH):
|
||||||
|
print(f"\n❌ 错误:图片1文件不存在: {IMAGE1_PATH}")
|
||||||
|
print(" 请检查文件路径是否正确")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not is_url(IMAGE2_PATH) and not os.path.exists(IMAGE2_PATH):
|
||||||
|
print(f"\n❌ 错误:图片2文件不存在: {IMAGE2_PATH}")
|
||||||
|
print(" 请检查文件路径是否正确")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n📋 配置信息:")
|
||||||
|
print(f" 图片1: {IMAGE1_PATH}")
|
||||||
|
print(f" 图片2: {IMAGE2_PATH}")
|
||||||
|
print(f" 提示词: {PROMPT}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 执行合成
|
||||||
|
result = multi_image_merge(
|
||||||
|
image1_path=IMAGE1_PATH,
|
||||||
|
image2_path=IMAGE2_PATH,
|
||||||
|
prompt=PROMPT,
|
||||||
|
output_filename=OUTPUT_FILENAME
|
||||||
|
)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("🎉 合成完成!")
|
||||||
|
print("="*60)
|
||||||
|
else:
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("❌ 合成失败,请检查错误信息")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
|
||||||
|
# ========== 参数配置区(请修改以下参数)==========
|
||||||
|
|
||||||
|
# 1. 第一张图片路径(支持本地文件路径或网络 URL)
|
||||||
|
IMAGE1_PATH = "5.png" # 本地文件示例: "1.png" 或 "C:/images/photo1.jpg"
|
||||||
|
# IMAGE1_PATH = "https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=800&q=80" # URL 示例
|
||||||
|
|
||||||
|
# 2. 第二张图片路径(支持本地文件路径或网络 URL)
|
||||||
|
IMAGE2_PATH = "6.png" # 本地文件示例: "2.png" 或 "C:/images/photo2.jpg"
|
||||||
|
# IMAGE2_PATH = "https://images.unsplash.com/photo-1560806887-1e4cd0b6cbd6?w=800&q=80" # URL 示例
|
||||||
|
|
||||||
|
# 3. 合成提示词(描述如何合成这两张图片)
|
||||||
|
PROMPT = "把第2张图的衣股花型图案替换成图1的花型图案" # 修改为您想要的合成效果描述
|
||||||
|
|
||||||
|
# 4. 输出文件名(可选,留空则使用默认名称)
|
||||||
|
OUTPUT_FILENAME = None # 例如: "合成结果.png" 或 None(使用默认名称)
|
||||||
|
|
||||||
|
# ================================================
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ const config: ICepConfig = {
|
|||||||
"panels": [
|
"panels": [
|
||||||
{
|
{
|
||||||
"name": "AdminPanel-dev",
|
"name": "AdminPanel-dev",
|
||||||
"displayName": "管理面板",
|
"displayName": "ps套版",
|
||||||
"main": "./index.html",
|
"main": "./index.html",
|
||||||
"width": 280,
|
"width": 280,
|
||||||
"height": 600,
|
"height": 600,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
<script src="/CSInterface.js"></script>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -78,6 +78,13 @@ export class CEP {
|
|||||||
this.writeDebug()
|
this.writeDebug()
|
||||||
this.copyJson2()
|
this.copyJson2()
|
||||||
|
|
||||||
|
// 确保 CEP 扩展目录存在
|
||||||
|
const cepExtDir = getAdobeCepDir()
|
||||||
|
if (!fs.existsSync(cepExtDir)) {
|
||||||
|
fs.mkdirSync(cepExtDir, { recursive: true })
|
||||||
|
console.log('[CEP] 已创建 CEP 扩展目录:', cepExtDir)
|
||||||
|
}
|
||||||
|
|
||||||
// 创建符号链接或直接复制
|
// 创建符号链接或直接复制
|
||||||
if (!fs.existsSync(this.cepLink)) {
|
if (!fs.existsSync(this.cepLink)) {
|
||||||
try {
|
try {
|
||||||
@@ -85,8 +92,8 @@ export class CEP {
|
|||||||
console.log('[CEP] 符号链接已创建')
|
console.log('[CEP] 符号链接已创建')
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// 权限不足时,改用复制
|
// 权限不足时,改用复制
|
||||||
if (error.code === 'EPERM') {
|
if (error.code === 'EPERM' || error.code === 'ENOENT') {
|
||||||
console.warn('[CEP] 符号链接权限不足,改用复制方式')
|
console.warn('[CEP] 符号链接创建失败,改用复制方式')
|
||||||
this.copyToCepDir()
|
this.copyToCepDir()
|
||||||
} else {
|
} else {
|
||||||
throw error
|
throw error
|
||||||
@@ -160,7 +167,7 @@ export class CEP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 复制并修正 HTML 路径
|
// 3. 复制并修正 HTML 路径
|
||||||
const builtHtmlPath = path.join(this.dist, 'src/launcher/index.html')
|
const builtHtmlPath = path.join(this.dist, 'index.html')
|
||||||
const targetHtmlPath = path.join(this.cepOutput, 'index.html')
|
const targetHtmlPath = path.join(this.cepOutput, 'index.html')
|
||||||
|
|
||||||
if (fs.existsSync(builtHtmlPath)) {
|
if (fs.existsSync(builtHtmlPath)) {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 475 B |
|
Before Width: | Height: | Size: 846 B After Width: | Height: | Size: 846 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 475 B |
|
Before Width: | Height: | Size: 846 B After Width: | Height: | Size: 846 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -5,8 +5,8 @@ export function joinHtml(name:string,server:string){
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>${name}</title>
|
<title>${name}</title>
|
||||||
<script>
|
<script>
|
||||||
// 直接跳转到目标网址(带时间戳防止缓存)
|
// 跳转到开发服务器地址
|
||||||
window.location.href = "https://app.aidg168.uk/?_t=" + Date.now();
|
window.location.href = "${server}";
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -5,10 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useTheme } from '@/hooks/useTheme'
|
// 主题初始化已在 main.ts 中完成
|
||||||
|
|
||||||
// 初始化主题
|
|
||||||
const { isDark } = useTheme()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { isNodeJSEnabled } from "./tool"
|
import { isNodeJSEnabled } from "./tool"
|
||||||
console.error('isNodeJSEnabled()'+isNodeJSEnabled());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cep_node 为ps内置的全局变量
|
* cep_node 为ps内置的全局变量
|
||||||
|
|||||||
@@ -107,8 +107,31 @@ export const updateTheme = () => {
|
|||||||
const isLight = brightness > 128;
|
const isLight = brightness > 128;
|
||||||
|
|
||||||
applyTheme(bgColorHex, isLight, skinInfo.baseFontSize);
|
applyTheme(bgColorHex, isLight, skinInfo.baseFontSize);
|
||||||
|
|
||||||
|
// 发送主题给 iframe
|
||||||
|
const iframe = document.querySelector('iframe');
|
||||||
|
if (iframe && iframe.contentWindow) {
|
||||||
|
const message: PSThemeMessage = {
|
||||||
|
type: 'PS_THEME',
|
||||||
|
theme: {
|
||||||
|
bgColor: bgColorHex,
|
||||||
|
isLight: isLight,
|
||||||
|
fontSize: skinInfo.baseFontSize
|
||||||
|
}
|
||||||
|
};
|
||||||
|
iframe.contentWindow.postMessage(message, '*');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理来自子 iframe 的请求
|
||||||
|
*/
|
||||||
|
function handleChildMessage(event: MessageEvent) {
|
||||||
|
if (event.data && event.data.type === 'REQUEST_THEME') {
|
||||||
|
updateTheme();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理来自父窗口的主题消息(iframe 模式)
|
* 处理来自父窗口的主题消息(iframe 模式)
|
||||||
*/
|
*/
|
||||||
@@ -138,6 +161,8 @@ export const initThemeListener = () => {
|
|||||||
logger.log('[Theme] CEP 模式 - 直接监听主题变化');
|
logger.log('[Theme] CEP 模式 - 直接监听主题变化');
|
||||||
updateTheme();
|
updateTheme();
|
||||||
cep.addEventListener('com.adobe.csxs.events.ThemeColorChanged', updateTheme);
|
cep.addEventListener('com.adobe.csxs.events.ThemeColorChanged', updateTheme);
|
||||||
|
// 监听来自 iframe 的请求
|
||||||
|
window.addEventListener('message', handleChildMessage);
|
||||||
} else {
|
} else {
|
||||||
// 纯浏览器模式
|
// 纯浏览器模式
|
||||||
logger.log('[Theme] 浏览器模式 - 使用默认主题');
|
logger.log('[Theme] 浏览器模式 - 使用默认主题');
|
||||||
|
|||||||
@@ -1,26 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="iframe-container">
|
<div class="iframe-container">
|
||||||
<!-- 顶部工具栏 -->
|
<!-- 顶部工具栏 (已移除) -->
|
||||||
<div class="toolbar">
|
|
||||||
<a-button size="small" @click="goBack">
|
|
||||||
<template #icon><icon-arrow-left /></template>
|
|
||||||
返回
|
|
||||||
</a-button>
|
|
||||||
<span class="url-display">{{ currentUrl }}</span>
|
|
||||||
<a-button size="small" @click="reload">
|
|
||||||
<template #icon><icon-refresh /></template>
|
|
||||||
刷新
|
|
||||||
</a-button>
|
|
||||||
<a-button size="small" @click="openExternal">
|
|
||||||
<template #icon><icon-export /></template>
|
|
||||||
外部打开
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 加载指示器 -->
|
<!-- 加载指示器 -->
|
||||||
<div v-if="loading" class="loading-overlay">
|
<div v-if="loading" class="loading-overlay">
|
||||||
<a-spin size="32" />
|
<a-spin size="32" />
|
||||||
<p>加载中...</p>
|
<p>加载中...</p>
|
||||||
|
<p style="font-size:12px;color:#999;margin-top:10px;">目标地址: {{ currentUrl }}</p>
|
||||||
|
<p style="font-size:12px;color:#999;">开发环境: {{ isDev ? '是' : '否' }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- iframe -->
|
<!-- iframe -->
|
||||||
@@ -36,39 +23,21 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { updateTheme } from '@/utils/theme'
|
||||||
import { IconArrowLeft, IconRefresh, IconExport } from '@arco-design/web-vue/es/icon'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const frameRef = ref<HTMLIFrameElement | null>(null)
|
const frameRef = ref<HTMLIFrameElement | null>(null)
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
|
const isDev = ref(typeof __DEV__ !== 'undefined' ? __DEV__ : false)
|
||||||
|
|
||||||
const currentUrl = ref(
|
const currentUrl = ref(
|
||||||
localStorage.getItem('adminPanelUrl') || (
|
localStorage.getItem('adminPanelUrl') || 'http://localhost:5173'
|
||||||
__DEV__
|
|
||||||
? 'http://localhost:5173'
|
|
||||||
: 'https://app.aidg168.uk'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
function goBack() {
|
|
||||||
router.push('/')
|
|
||||||
}
|
|
||||||
|
|
||||||
function reload() {
|
|
||||||
loading.value = true
|
|
||||||
if (frameRef.value) {
|
|
||||||
frameRef.value.src = currentUrl.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function openExternal() {
|
|
||||||
window.open(currentUrl.value, '_blank')
|
|
||||||
}
|
|
||||||
|
|
||||||
function onFrameLoad() {
|
function onFrameLoad() {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
console.log('✅ iframe 加载完成')
|
console.log('✅ iframe 加载完成')
|
||||||
|
// iframe 加载完成后立即同步主题
|
||||||
|
updateTheme()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFrameError(e: Event) {
|
function onFrameError(e: Event) {
|
||||||
@@ -94,24 +63,6 @@ onMounted(() => {
|
|||||||
background: var(--ps-bg, #1e1e1e);
|
background: var(--ps-bg, #1e1e1e);
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
background: var(--color-bg-2, #2d2d2d);
|
|
||||||
border-bottom: 1px solid var(--ps-border, #444);
|
|
||||||
}
|
|
||||||
|
|
||||||
.url-display {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--ps-text, #aaa);
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-frame {
|
.main-frame {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default defineConfig(({ command }) => {
|
|||||||
legacy({
|
legacy({
|
||||||
targets: ['chrome 41', 'not IE 11']
|
targets: ['chrome 41', 'not IE 11']
|
||||||
}),
|
}),
|
||||||
!isBuild && cepPlugin(), // 开发时自动复制到 PS 扩展目录
|
cepPlugin(), // 开发时自动复制到 PS 扩展目录,构建时生成 CEP 结构
|
||||||
vue(),
|
vue(),
|
||||||
tsconfigPaths(),
|
tsconfigPaths(),
|
||||||
],
|
],
|
||||||
|
|||||||
225
PSMARK代码块/44222.jsx
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
|
||||||
|
贴图换图()
|
||||||
|
function 贴图换图() {
|
||||||
|
建立快照();
|
||||||
|
var folder = Folder.selectDialog("请选择一个文件夹");
|
||||||
|
var 模特文档 = app.activeDocument;
|
||||||
|
|
||||||
|
if (folder) {
|
||||||
|
// 在选择的文件夹中创建一个新的子文件夹
|
||||||
|
var targetFolder = new Folder(folder.fullName + "/贴图生成");
|
||||||
|
if (!targetFolder.exists) {
|
||||||
|
targetFolder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileNames = [];
|
||||||
|
var files = folder.getFiles();
|
||||||
|
|
||||||
|
// 获取文件夹中的所有文件名
|
||||||
|
for (var i = 0; i < files.length; i++) {
|
||||||
|
if (files[i] instanceof File) {
|
||||||
|
fileNames.push(files[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取“贴图位置”图层组中的所有图层名称
|
||||||
|
var layerSet = 模特文档.layerSets.getByName("贴图位置");
|
||||||
|
var layerNames = [];
|
||||||
|
for (var j = 0; j < layerSet.artLayers.length; j++) {
|
||||||
|
layerNames.push(layerSet.artLayers[j].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历“贴图位置”图层组中的每个图层
|
||||||
|
for (var k = 0; k < layerNames.length; k++) {
|
||||||
|
var layname = layerNames[k];
|
||||||
|
var 贴图位置 = app.activeDocument.layerSets.getByName("贴图位置").layers.getByName(layname);
|
||||||
|
app.activeDocument.activeLayer = 贴图位置;
|
||||||
|
|
||||||
|
// 依次调取文件夹中的文件
|
||||||
|
if (k < fileNames.length) {
|
||||||
|
var file = new File(folder + "/" + fileNames[k]);
|
||||||
|
if (file.exists) {
|
||||||
|
try {
|
||||||
|
app.open(file);
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
var 当前文档 = app.activeDocument;
|
||||||
|
var 当前文档名称 = 当前文档.name;
|
||||||
|
预设图案(当前文档名称); // 需要自定义实现
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
app.activeDocument = 模特文档;
|
||||||
|
载入选区(); // 需要自定义实现
|
||||||
|
填充图案(当前文档名称); // 需要自定义实现
|
||||||
|
} catch (e) {
|
||||||
|
alert("无法打开文件: " + fileNames[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存文件为 TIF 格式
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
var 当前文档 = app.activeDocument; // 获取当前文档
|
||||||
|
var 当前文档名称 = 当前文档.name; // 获取当前文档名称(包含扩展名)
|
||||||
|
|
||||||
|
// 去除文件扩展名并添加 ".tif" 后缀
|
||||||
|
var 文件名 = 当前文档名称.replace(/\.[^\.]+$/, "") + ".tif";
|
||||||
|
|
||||||
|
// 创建保存路径的 File 对象
|
||||||
|
var saveFile = new File(当前文档.path + "/" + 文件名);
|
||||||
|
|
||||||
|
// 设置 Tiff 保存选项
|
||||||
|
var tiffSaveOptions = new TiffSaveOptions();
|
||||||
|
tiffSaveOptions.imageCompression = TIFFEncoding.NONE; // 无压缩
|
||||||
|
tiffSaveOptions.alphaChannels = true; // 保留 alpha 通道
|
||||||
|
tiffSaveOptions.layers = true; // 保留图层
|
||||||
|
|
||||||
|
// 另存为 TIF 格式
|
||||||
|
当前文档.saveAs(saveFile, tiffSaveOptions, true, Extension.LOWERCASE);
|
||||||
|
alert("换图完成");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 其他函数保持不变
|
||||||
|
|
||||||
|
|
||||||
|
function generateRandomNumber(min, max) {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 图像大小() //图像大小
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), 150);
|
||||||
|
d.putBoolean(stringIDToTypeID("scaleStyles"), true);
|
||||||
|
d.putBoolean(stringIDToTypeID("constrainProportions"), true);
|
||||||
|
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||||
|
executeAction(stringIDToTypeID("imageSize"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 取消链接蒙版() //取消链接蒙版
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putBoolean(stringIDToTypeID("userMaskLinked"), false);
|
||||||
|
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 栅格化图层() //栅格化图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("rasterizeLayer"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 预设图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("pattern"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||||
|
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 载入选区() //载入选区
|
||||||
|
{
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 填充图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("contentLayer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
var d2 = new ActionDescriptor();
|
||||||
|
var d3 = new ActionDescriptor();
|
||||||
|
d3.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
|
||||||
|
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||||
|
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 位移(水平,垂直) //位移
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putInteger(stringIDToTypeID("horizontal"), 水平);
|
||||||
|
d.putInteger(stringIDToTypeID("vertical"), 垂直);
|
||||||
|
d.putEnumerated(stringIDToTypeID("fill"), stringIDToTypeID("fillMode"), stringIDToTypeID("wrap"));
|
||||||
|
executeAction(stringIDToTypeID("offset"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 历史记录回退到快照1() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("snapshotClass"), "快照 1");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 建立快照() //打开
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("snapshotClass"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("historyState"), stringIDToTypeID("currentHistoryState"));
|
||||||
|
d.putReference(stringIDToTypeID("from"), r1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
1001
PSMARK代码块/JSX17.jsx
Normal file
80
PSMARK代码块/JSX监听单个事件-漂流鱼.jsx
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
//JSX监听某个事件
|
||||||
|
|
||||||
|
alert("当前监听事件个数:"+app.notifiers.length,"提示:")
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
arg_num = arguments.length;
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
arguments = []; //初始赋值为空
|
||||||
|
}
|
||||||
|
|
||||||
|
eventName = "recordMeasurements";
|
||||||
|
|
||||||
|
//开启监听
|
||||||
|
enable_notifier(eventName, $.fileName);
|
||||||
|
|
||||||
|
//取消监听
|
||||||
|
//~ disable_notifier(eventName, $.fileName);
|
||||||
|
|
||||||
|
//这里进行监控调用事件
|
||||||
|
if (arguments.length >= 2)
|
||||||
|
{
|
||||||
|
//alert(arguments.length);
|
||||||
|
//~ alert(arguments[0],"动作参数1"); //动作描述符 AR
|
||||||
|
//~ alert(arguments[1],"动作参数2"); //动作事件ID
|
||||||
|
|
||||||
|
alert("事件名:"+typeIDToStringID(arguments[1])+"\n"+"事件ID:"+arguments[1],"提示:");
|
||||||
|
|
||||||
|
//main(arguments[0], arguments[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function enable_notifier(event_name, script_name, event_class)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (var i = 0; i < app.notifiers.length; i++)
|
||||||
|
{
|
||||||
|
if (app.notifiers[i].event == event_name &&
|
||||||
|
File(app.notifiers[i].eventFile).fsName.toLowerCase() == File(script_name).fsName.toLowerCase())
|
||||||
|
{
|
||||||
|
if (!app.notifiersEnabled) app.notifiersEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.notifiers.add(event_name, File(script_name), event_class);
|
||||||
|
app.notifiersEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (e) { _alert(e); return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function disable_notifier(event_name, script_name, event_class)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ret = false;
|
||||||
|
|
||||||
|
for (var i = 0; i < app.notifiers.length; i++)
|
||||||
|
{
|
||||||
|
if (app.notifiers[i].event == event_name &&
|
||||||
|
File(app.notifiers[i].eventFile).fsName.toLowerCase() == File(script_name).fsName.toLowerCase())
|
||||||
|
{
|
||||||
|
app.notifiers[i].remove();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!app.notifiers.length) app.notifiersEnabled = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (e) { _alert(e); return false; }
|
||||||
|
}
|
||||||
627
PSMARK代码块/PNG素材拆分图层 - 函数.jsx
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
// photoshopscripts.wordpress.com
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// Split to Layers 1.0 //
|
||||||
|
// 2012, David Jensen //
|
||||||
|
// //
|
||||||
|
// With help from //
|
||||||
|
// pfaffenbichler and xbytor //
|
||||||
|
// at ps-scripts.com //
|
||||||
|
////////////////////////////////////
|
||||||
|
|
||||||
|
#target photoshop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//更改以下 5 个值中的任何一个以自定义脚本的默认选项:
|
||||||
|
|
||||||
|
var showOptionsDialog = true; //设置为 false 以禁用对用户的提示.
|
||||||
|
var tolerance = 2; // 将被忽略的透明像素的最大间隙,设置默认值.
|
||||||
|
var confirmThreshold = 20; // 如果脚本要制作大量图层,提示用户确认这是可以的.
|
||||||
|
var suffix = "-"; // 将此添加到新图层的图层名称中. 设置为空不添加.
|
||||||
|
var addCount = true; // 在每个新层的末尾添加一个增量数字.
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
///////////////////////////////////////
|
||||||
|
///////////////////////////////////////
|
||||||
|
///////////////////////////////////////
|
||||||
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 裁片分解()
|
||||||
|
{
|
||||||
|
var layerNamePreview=activeDocument.activeLayer.name + suffix;
|
||||||
|
if (addCount === true){
|
||||||
|
layerNamePreview += "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalRulerUnits = app.preferences.rulerUnits;
|
||||||
|
app.preferences.rulerUnits = Units.POINTS;
|
||||||
|
|
||||||
|
bounds = activeDocument.activeLayer.bounds
|
||||||
|
var emptyLayer=false;
|
||||||
|
if (Number(bounds[0]) == 0 && Number(bounds[1]) == 0 && Number(bounds[2]) == 0 && Number(bounds[3]) == 0) {emptyLayer = true};
|
||||||
|
|
||||||
|
try{
|
||||||
|
if (activeDocument.activeLayer.kind != undefined && activeDocument.activeLayer.isBackgroundLayer == false && emptyLayer == false){
|
||||||
|
activeDocument.suspendHistory("Separate", "main()");
|
||||||
|
//~ app.doForcedProgress("PSMark-裁片分解","PSMark_main()");
|
||||||
|
//~ app.doProgress("PSMark-裁片分解","PSMark_main()");
|
||||||
|
//~ app.doForcedProgress("PSMark-裁片分解","main()");
|
||||||
|
}else{
|
||||||
|
alert( "未选择支持的图层类型.");
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
alert(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.preferences.rulerUnits = originalRulerUnits;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function PSMark_main()
|
||||||
|
{
|
||||||
|
app.activeDocument.suspendHistory("Separate", "main()");
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
|
||||||
|
baseLayer=activeDocument.activeLayer;
|
||||||
|
activeDocument.quickMaskMode = false;
|
||||||
|
activeDocument.selection.deselect()
|
||||||
|
var layerName = activeDocument.activeLayer.name
|
||||||
|
//if a selection can't be made, stop running the script
|
||||||
|
|
||||||
|
|
||||||
|
var idCpTL = charIDToTypeID("CpTL");
|
||||||
|
executeAction(idCpTL, undefined, DialogModes.NO);
|
||||||
|
|
||||||
|
activeDocument.activeLayer.rasterize(RasterizeType.ENTIRELAYER)
|
||||||
|
try{
|
||||||
|
var idDlt = charIDToTypeID( "Dlt " );
|
||||||
|
var desc120 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID( "null" );
|
||||||
|
var ref112 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID( "Chnl" );
|
||||||
|
var idChnl = charIDToTypeID( "Chnl" );
|
||||||
|
var idMsk = charIDToTypeID( "Msk " );
|
||||||
|
ref112.putEnumerated( idChnl, idChnl, idMsk );
|
||||||
|
desc120.putReference( idnull, ref112 );
|
||||||
|
var idAply = charIDToTypeID( "Aply" );
|
||||||
|
desc120.putBoolean( idAply, true );
|
||||||
|
executeAction( idDlt, desc120, DialogModes.NO );
|
||||||
|
}catch(e){}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer.name = layerName;
|
||||||
|
|
||||||
|
baseLayer=activeDocument.activeLayer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
makeSelection();
|
||||||
|
|
||||||
|
var idMk = charIDToTypeID("Mk ");
|
||||||
|
var desc642 = new ActionDescriptor();
|
||||||
|
var idNw = charIDToTypeID("Nw ");
|
||||||
|
var idDcmn = charIDToTypeID("Dcmn");
|
||||||
|
desc642.putClass(idNw, idDcmn);
|
||||||
|
var idUsng = charIDToTypeID("Usng");
|
||||||
|
var ref535 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref535.putEnumerated(idChnl, idOrdn, idTrgt);
|
||||||
|
desc642.putReference(idUsng, ref535);
|
||||||
|
executeAction(idMk, desc642, DialogModes.NO);
|
||||||
|
|
||||||
|
newDoc = activeDocument;
|
||||||
|
// =======================================================
|
||||||
|
activeDocument.resizeImage("200%", "200%", undefined, ResampleMethod.NEARESTNEIGHBOR);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc934 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref535 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref535.putProperty(idChnl, idfsel);
|
||||||
|
desc934.putReference(idnull, ref535);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var ref536 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref536.putEnumerated(idChnl, idOrdn, idTrgt);
|
||||||
|
desc934.putReference(idT, ref536);
|
||||||
|
executeAction(idsetd, desc934, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
var idMk = charIDToTypeID("Mk ");
|
||||||
|
var desc403 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref288 = new ActionReference();
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
ref288.putClass(idPath);
|
||||||
|
desc403.putReference(idnull, ref288);
|
||||||
|
var idFrom = charIDToTypeID("From");
|
||||||
|
var ref289 = new ActionReference();
|
||||||
|
var idcsel = charIDToTypeID("csel");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref289.putProperty(idcsel, idfsel);
|
||||||
|
desc403.putReference(idFrom, ref289);
|
||||||
|
var idTlrn = charIDToTypeID("Tlrn");
|
||||||
|
var idPxl = charIDToTypeID("#Pxl");
|
||||||
|
desc403.putUnitDouble(idTlrn, idPxl, 0.500000);
|
||||||
|
executeAction(idMk, desc403, DialogModes.NO);
|
||||||
|
|
||||||
|
var subPathsLength = activeDocument.pathItems[0].subPathItems.length
|
||||||
|
|
||||||
|
if (subPathsLength>confirmThreshold){
|
||||||
|
var answer = confirm("基于"+subPathsLength+ "个拆分对象将创建图层. 你想继续吗?",true)
|
||||||
|
if (answer === false){
|
||||||
|
newDoc.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
activeDocument.quickMaskMode = false;
|
||||||
|
activeDocument.selection.deselect();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
activeDocument.resizeImage("50%", "50%", undefined, ResampleMethod.NEARESTNEIGHBOR)
|
||||||
|
|
||||||
|
var pathInfo = collectPathInfoFromDesc(activeDocument, activeDocument.pathItems[activeDocument.pathItems.length - 1])
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
newDoc.close(SaveOptions.DONOTSAVECHANGES)
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
activeDocument.quickMaskMode = false
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
//make channel
|
||||||
|
// =======================================================
|
||||||
|
var idMk = charIDToTypeID("Mk ");
|
||||||
|
var desc6 = new ActionDescriptor();
|
||||||
|
var idNw = charIDToTypeID("Nw ");
|
||||||
|
var desc7 = new ActionDescriptor();
|
||||||
|
var idNm = charIDToTypeID("Nm ");
|
||||||
|
desc7.putString(idNm, "ContiguityMask");
|
||||||
|
var idClrI = charIDToTypeID("ClrI");
|
||||||
|
var idMskI = charIDToTypeID("MskI");
|
||||||
|
var idMskA = charIDToTypeID("MskA");
|
||||||
|
desc7.putEnumerated(idClrI, idMskI, idMskA);
|
||||||
|
var idClr = charIDToTypeID("Clr ");
|
||||||
|
var desc8 = new ActionDescriptor();
|
||||||
|
var idRd = charIDToTypeID("Rd ");
|
||||||
|
desc8.putDouble(idRd, 255.000000);
|
||||||
|
var idGrn = charIDToTypeID("Grn ");
|
||||||
|
desc8.putDouble(idGrn, 0.000000);
|
||||||
|
var idBl = charIDToTypeID("Bl ");
|
||||||
|
desc8.putDouble(idBl, 0.000000);
|
||||||
|
var idRGBC = charIDToTypeID("RGBC");
|
||||||
|
desc7.putObject(idClr, idRGBC, desc8);
|
||||||
|
var idOpct = charIDToTypeID("Opct");
|
||||||
|
desc7.putInteger(idOpct, 50);
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
desc6.putObject(idNw, idChnl, desc7);
|
||||||
|
var idUsng = charIDToTypeID("Usng");
|
||||||
|
var ref5 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref5.putProperty(idChnl, idfsel);
|
||||||
|
desc6.putReference(idUsng, ref5);
|
||||||
|
executeAction(idMk, desc6, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
doc_name = app.activeDocument.name.replace(/(?:\.[^.]*$|$)/, '');
|
||||||
|
|
||||||
|
var layerCount = 1
|
||||||
|
for (i = 0; i < subPathsLength; i++) {
|
||||||
|
//deselect
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc279 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref137 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref137.putProperty(idChnl, idfsel);
|
||||||
|
desc279.putReference(idnull, ref137);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idNone = charIDToTypeID("None");
|
||||||
|
desc279.putEnumerated(idT, idOrdn, idNone);
|
||||||
|
executeAction(idsetd, desc279, DialogModes.NO);
|
||||||
|
///select alpha channel
|
||||||
|
var idslct = charIDToTypeID("slct");
|
||||||
|
var desc315 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref175 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
ref175.putName(idChnl, "ContiguityMask");
|
||||||
|
desc315.putReference(idnull, ref175);
|
||||||
|
executeAction(idslct, desc315, DialogModes.NO);
|
||||||
|
//use magic wand
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc263 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref123 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref123.putProperty(idChnl, idfsel);
|
||||||
|
desc263.putReference(idnull, ref123);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var desc264 = new ActionDescriptor();
|
||||||
|
var idHrzn = charIDToTypeID("Hrzn");
|
||||||
|
var idRlt = charIDToTypeID("#Rlt");
|
||||||
|
desc264.putUnitDouble(idHrzn, idRlt, pathInfo[i][0][0]);
|
||||||
|
var idVrtc = charIDToTypeID("Vrtc");
|
||||||
|
var idRlt = charIDToTypeID("#Rlt");
|
||||||
|
|
||||||
|
desc264.putUnitDouble(idVrtc, idRlt, pathInfo[i][0][1]);
|
||||||
|
var idPnt = charIDToTypeID("Pnt ");
|
||||||
|
desc263.putObject(idT, idPnt, desc264);
|
||||||
|
var idTlrn = charIDToTypeID("Tlrn");
|
||||||
|
desc263.putInteger(idTlrn, 1);
|
||||||
|
executeAction(idsetd, desc263, DialogModes.NO);
|
||||||
|
|
||||||
|
var idslct = charIDToTypeID("slct");
|
||||||
|
var desc346 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref205 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idRGB = charIDToTypeID("RGB ");
|
||||||
|
ref205.putEnumerated(idChnl, idChnl, idRGB);
|
||||||
|
desc346.putReference(idnull, ref205);
|
||||||
|
var idMkVs = charIDToTypeID("MkVs");
|
||||||
|
desc346.putBoolean(idMkVs, false);
|
||||||
|
executeAction(idslct, desc346, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
// =======================================================
|
||||||
|
var idCpTL = charIDToTypeID("CpTL");
|
||||||
|
executeAction(idCpTL, undefined, DialogModes.NO);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var idrasterizeLayer = stringIDToTypeID("rasterizeLayer");
|
||||||
|
var desc924 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref721 = new ActionReference();
|
||||||
|
var idLyr = charIDToTypeID("Lyr ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref721.putEnumerated(idLyr, idOrdn, idTrgt);
|
||||||
|
desc924.putReference(idnull, ref721);
|
||||||
|
var idWhat = charIDToTypeID("What");
|
||||||
|
var idrasterizeItem = stringIDToTypeID("rasterizeItem");
|
||||||
|
var idvectorMask = stringIDToTypeID("vectorMask");
|
||||||
|
desc924.putEnumerated(idWhat, idrasterizeItem, idvectorMask);
|
||||||
|
executeAction(idrasterizeLayer, desc924, DialogModes.NO);
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var idIntr = charIDToTypeID("Intr");
|
||||||
|
var desc864 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref658 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref658.putEnumerated(idChnl, idOrdn, idTrgt);
|
||||||
|
desc864.putReference(idnull, ref658);
|
||||||
|
var idWith = charIDToTypeID("With");
|
||||||
|
var ref659 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref659.putProperty(idChnl, idfsel);
|
||||||
|
desc864.putReference(idWith, ref659);
|
||||||
|
executeAction(idIntr, desc864, DialogModes.NO);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idDlt = charIDToTypeID("Dlt ");
|
||||||
|
var desc865 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref660 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref660.putEnumerated(idChnl, idOrdn, idTrgt);
|
||||||
|
desc865.putReference(idnull, ref660);
|
||||||
|
executeAction(idDlt, desc865, DialogModes.NO);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idMk = charIDToTypeID("Mk ");
|
||||||
|
var desc866 = new ActionDescriptor();
|
||||||
|
var idNw = charIDToTypeID("Nw ");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
desc866.putClass(idNw, idChnl);
|
||||||
|
var idAt = charIDToTypeID("At ");
|
||||||
|
var ref661 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idMsk = charIDToTypeID("Msk ");
|
||||||
|
ref661.putEnumerated(idChnl, idChnl, idMsk);
|
||||||
|
desc866.putReference(idAt, ref661);
|
||||||
|
var idUsng = charIDToTypeID("Usng");
|
||||||
|
var idUsrM = charIDToTypeID("UsrM");
|
||||||
|
var idRvlS = charIDToTypeID("RvlS");
|
||||||
|
desc866.putEnumerated(idUsng, idUsrM, idRvlS);
|
||||||
|
executeAction(idMk, desc866, DialogModes.NO);
|
||||||
|
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
var finalSuffix = suffix;
|
||||||
|
if (addCount===true)
|
||||||
|
{
|
||||||
|
finalSuffix += layerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//~ activeDocument.activeLayer.name = layerName + finalSuffix;
|
||||||
|
//分解的图层命名
|
||||||
|
activeDocument.activeLayer.name = "Mark" +"-" + doc_name + finalSuffix;
|
||||||
|
layerCount++;
|
||||||
|
|
||||||
|
|
||||||
|
//~ $.writeln(100*(layerCount-1)/subPathsLength);
|
||||||
|
//~ $.writeln("subPathsLength:"+subPathsLength);
|
||||||
|
//~ app.updateProgress(100*(layerCount-1)/subPathsLength,100);
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer=baseLayer;
|
||||||
|
}
|
||||||
|
catch (e) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc1045 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref578 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref578.putProperty(idChnl, idfsel);
|
||||||
|
desc1045.putReference(idnull, ref578);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idNone = charIDToTypeID("None");
|
||||||
|
desc1045.putEnumerated(idT, idOrdn, idNone);
|
||||||
|
executeAction(idsetd, desc1045, DialogModes.NO);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idDlt = charIDToTypeID("Dlt ");
|
||||||
|
var desc694 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref323 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
ref323.putName(idChnl, "ContiguityMask");
|
||||||
|
desc694.putReference(idnull, ref323);
|
||||||
|
executeAction(idDlt, desc694, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer.remove();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var idHd = charIDToTypeID("Hd ");
|
||||||
|
var desc736 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var list22 = new ActionList();
|
||||||
|
var ref541 = new ActionReference();
|
||||||
|
var idLyr = charIDToTypeID("Lyr ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref541.putEnumerated(idLyr, idOrdn, idTrgt);
|
||||||
|
list22.putReference(ref541);
|
||||||
|
desc736.putList(idnull, list22);
|
||||||
|
executeAction(idHd, desc736, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// pfaffenbichler and xbytor //
|
||||||
|
// at ps-scripts.com //
|
||||||
|
// created this function //
|
||||||
|
function collectPathInfoFromDesc(myDocument, thePath) {
|
||||||
|
var myDocument = app.activeDocument;
|
||||||
|
|
||||||
|
// based of functions from xbytor’s stdlib;
|
||||||
|
var ref = new ActionReference();
|
||||||
|
for (var l = 0; l < myDocument.pathItems.length; l++) {
|
||||||
|
var thisPath = myDocument.pathItems[l];
|
||||||
|
if (thisPath == thePath && thisPath.name == "Work Path") {
|
||||||
|
ref.putProperty(cTID("Path"), cTID("WrPt"));
|
||||||
|
};
|
||||||
|
if (thisPath == thePath && thisPath.name != "Work Path" && thisPath.kind != PathKind.VECTORMASK) {
|
||||||
|
ref.putIndex(cTID("Path"), l + 1);
|
||||||
|
};
|
||||||
|
if (thisPath == thePath && thisPath.kind == PathKind.VECTORMASK) {
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
var idvectorMask = stringIDToTypeID("vectorMask");
|
||||||
|
ref.putEnumerated(idPath, idPath, idvectorMask);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var desc = app.executeActionGet(ref);
|
||||||
|
var pname = desc.getString(cTID('PthN'));
|
||||||
|
// create new array;
|
||||||
|
var theArray = new Array;
|
||||||
|
var pathComponents = desc.getObjectValue(cTID("PthC")).getList(sTID('pathComponents'));
|
||||||
|
// for subpathitems;
|
||||||
|
for (var m = 0; m < pathComponents.count; m++) {
|
||||||
|
var listKey = pathComponents.getObjectValue(m).getList(sTID("subpathListKey"));
|
||||||
|
// for subpathitem’s count;
|
||||||
|
for (var n = 0; n < listKey.count; n++) {
|
||||||
|
var points = listKey.getObjectValue(n).getList(sTID('points'));
|
||||||
|
// get first point;
|
||||||
|
var anchorObj = points.getObjectValue(0).getObjectValue(sTID("anchor"));
|
||||||
|
var anchor = [anchorObj.getUnitDoubleValue(sTID('horizontal')), anchorObj.getUnitDoubleValue(sTID('vertical'))];
|
||||||
|
var thisPoint = [anchor];
|
||||||
|
theArray.push(thisPoint);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// by xbytor, thanks to him;
|
||||||
|
|
||||||
|
|
||||||
|
function cTID(s) {
|
||||||
|
return cTID[s] || cTID[s] = app.charIDToTypeID(s);
|
||||||
|
};
|
||||||
|
|
||||||
|
function sTID(s) {
|
||||||
|
return sTID[s] || sTID[s] = app.stringIDToTypeID(s);
|
||||||
|
};
|
||||||
|
// reset;
|
||||||
|
return theArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function makePreviewSelection(){
|
||||||
|
makeSelection()
|
||||||
|
app.refresh()
|
||||||
|
activeDocument.quickMaskMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSelection(){
|
||||||
|
try{
|
||||||
|
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc922 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref529 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref529.putProperty(idChnl, idfsel);
|
||||||
|
desc922.putReference(idnull, ref529);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var ref530 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idTrsp = charIDToTypeID("Trsp");
|
||||||
|
ref530.putEnumerated(idChnl, idChnl, idTrsp);
|
||||||
|
desc922.putReference(idT, ref530);
|
||||||
|
executeAction(idsetd, desc922, DialogModes.NO);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
var idIntr = charIDToTypeID("Intr");
|
||||||
|
var desc846 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref639 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idMsk = charIDToTypeID("Msk ");
|
||||||
|
ref639.putEnumerated(idChnl, idChnl, idMsk);
|
||||||
|
desc846.putReference(idnull, ref639);
|
||||||
|
var idWith = charIDToTypeID("With");
|
||||||
|
var ref640 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref640.putProperty(idChnl, idfsel);
|
||||||
|
desc846.putReference(idWith, ref640);
|
||||||
|
executeAction(idIntr, desc846, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// =======================================================
|
||||||
|
var idIntW = charIDToTypeID("IntW");
|
||||||
|
var desc787 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref572 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref572.putProperty(idChnl, idfsel);
|
||||||
|
desc787.putReference(idnull, ref572);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var ref573 = new ActionReference();
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
var idvectorMask = stringIDToTypeID("vectorMask");
|
||||||
|
ref573.putEnumerated(idPath, idPath, idvectorMask);
|
||||||
|
var idLyr = charIDToTypeID("Lyr ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref573.putEnumerated(idLyr, idOrdn, idTrgt);
|
||||||
|
desc787.putReference(idT, ref573);
|
||||||
|
var idVrsn = charIDToTypeID("Vrsn");
|
||||||
|
desc787.putInteger(idVrsn, 1);
|
||||||
|
var idvectorMaskParams = stringIDToTypeID("vectorMaskParams");
|
||||||
|
desc787.putBoolean(idvectorMaskParams, true);
|
||||||
|
executeAction(idIntW, desc787, DialogModes.NO);
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (tolerance >= 2) {
|
||||||
|
|
||||||
|
activeDocument.selection.expand(Math.floor(tolerance / 2))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.quickMaskMode = true;
|
||||||
|
|
||||||
|
|
||||||
|
var idThrs = charIDToTypeID("Thrs");
|
||||||
|
var desc479 = new ActionDescriptor();
|
||||||
|
var idLvl = charIDToTypeID("Lvl ");
|
||||||
|
desc479.putInteger(idLvl, 1);
|
||||||
|
executeAction(idThrs, desc479, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (tolerance % 2 == 1) {
|
||||||
|
|
||||||
|
var idMtnB = charIDToTypeID("MtnB");
|
||||||
|
var desc213 = new ActionDescriptor();
|
||||||
|
var idAngl = charIDToTypeID("Angl");
|
||||||
|
desc213.putInteger(idAngl, 0);
|
||||||
|
var idDstn = charIDToTypeID("Dstn");
|
||||||
|
var idPxl = charIDToTypeID("#Pxl");
|
||||||
|
desc213.putUnitDouble(idDstn, idPxl, 1.000000);
|
||||||
|
executeAction(idMtnB, desc213, DialogModes.NO);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idMtnB = charIDToTypeID("MtnB");
|
||||||
|
var desc214 = new ActionDescriptor();
|
||||||
|
var idAngl = charIDToTypeID("Angl");
|
||||||
|
desc214.putInteger(idAngl, 90);
|
||||||
|
var idDstn = charIDToTypeID("Dstn");
|
||||||
|
var idPxl = charIDToTypeID("#Pxl");
|
||||||
|
desc214.putUnitDouble(idDstn, idPxl, 1.000000);
|
||||||
|
executeAction(idMtnB, desc214, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idThrs = charIDToTypeID("Thrs");
|
||||||
|
var desc215 = new ActionDescriptor();
|
||||||
|
var idLvl = charIDToTypeID("Lvl ");
|
||||||
|
desc215.putInteger(idLvl, 1);
|
||||||
|
executeAction(idThrs, desc215, DialogModes.NO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
673
PSMARK代码块/PNG素材拆分图层.jsx
Normal file
@@ -0,0 +1,673 @@
|
|||||||
|
// photoshopscripts.wordpress.com
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// Split to Layers 1.0 //
|
||||||
|
// 2012, David Jensen //
|
||||||
|
// //
|
||||||
|
// With help from //
|
||||||
|
// pfaffenbichler and xbytor //
|
||||||
|
// at ps-scripts.com //
|
||||||
|
////////////////////////////////////
|
||||||
|
|
||||||
|
#target photoshop
|
||||||
|
|
||||||
|
//更改以下 5 个值中的任何一个以自定义脚本的默认选项:
|
||||||
|
|
||||||
|
var showOptionsDialog = true; //设置为 false 以禁用对用户的提示.
|
||||||
|
var tolerance = 2; // 将被忽略的透明像素的最大间隙,设置默认值.
|
||||||
|
var confirmThreshold = 20; // 如果脚本要制作大量图层,提示用户确认这是可以的.
|
||||||
|
var suffix = "-"; // 将此添加到新图层的图层名称中. 设置为空不添加.
|
||||||
|
var addCount = true; // 在每个新层的末尾添加一个增量数字.
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
///////////////////////////////////////
|
||||||
|
///////////////////////////////////////
|
||||||
|
///////////////////////////////////////
|
||||||
|
///////////////////////////////////////
|
||||||
|
var layerNamePreview=activeDocument.activeLayer.name + suffix;
|
||||||
|
if (addCount === true){
|
||||||
|
layerNamePreview += "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalRulerUnits = app.preferences.rulerUnits;
|
||||||
|
app.preferences.rulerUnits = Units.POINTS;
|
||||||
|
|
||||||
|
bounds = activeDocument.activeLayer.bounds
|
||||||
|
var emptyLayer=false;
|
||||||
|
if (Number(bounds[0]) == 0 && Number(bounds[1]) == 0 && Number(bounds[2]) == 0 && Number(bounds[3]) == 0) {emptyLayer = true};
|
||||||
|
|
||||||
|
try{
|
||||||
|
if (activeDocument.activeLayer.kind != undefined && activeDocument.activeLayer.isBackgroundLayer == false && emptyLayer == false){
|
||||||
|
|
||||||
|
|
||||||
|
app.doForcedProgress("PSMark-裁片分解","PSMark_main()");
|
||||||
|
|
||||||
|
}else{
|
||||||
|
alert( "未选择支持的图层类型.");
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
alert(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.preferences.rulerUnits = originalRulerUnits;
|
||||||
|
|
||||||
|
|
||||||
|
function PSMark_main()
|
||||||
|
{
|
||||||
|
app.activeDocument.suspendHistory("Separate", "main()");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
|
||||||
|
var ok=createDialog();
|
||||||
|
if (ok===2){
|
||||||
|
activeDocument.selection.deselect()
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
baseLayer=activeDocument.activeLayer;
|
||||||
|
activeDocument.quickMaskMode = false;
|
||||||
|
activeDocument.selection.deselect()
|
||||||
|
var layerName = activeDocument.activeLayer.name
|
||||||
|
//if a selection can't be made, stop running the script
|
||||||
|
|
||||||
|
|
||||||
|
var idCpTL = charIDToTypeID("CpTL");
|
||||||
|
executeAction(idCpTL, undefined, DialogModes.NO);
|
||||||
|
|
||||||
|
activeDocument.activeLayer.rasterize(RasterizeType.ENTIRELAYER)
|
||||||
|
try{
|
||||||
|
var idDlt = charIDToTypeID( "Dlt " );
|
||||||
|
var desc120 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID( "null" );
|
||||||
|
var ref112 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID( "Chnl" );
|
||||||
|
var idChnl = charIDToTypeID( "Chnl" );
|
||||||
|
var idMsk = charIDToTypeID( "Msk " );
|
||||||
|
ref112.putEnumerated( idChnl, idChnl, idMsk );
|
||||||
|
desc120.putReference( idnull, ref112 );
|
||||||
|
var idAply = charIDToTypeID( "Aply" );
|
||||||
|
desc120.putBoolean( idAply, true );
|
||||||
|
executeAction( idDlt, desc120, DialogModes.NO );
|
||||||
|
}catch(e){}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer.name = layerName
|
||||||
|
|
||||||
|
baseLayer=activeDocument.activeLayer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
makeSelection()
|
||||||
|
|
||||||
|
var idMk = charIDToTypeID("Mk ");
|
||||||
|
var desc642 = new ActionDescriptor();
|
||||||
|
var idNw = charIDToTypeID("Nw ");
|
||||||
|
var idDcmn = charIDToTypeID("Dcmn");
|
||||||
|
desc642.putClass(idNw, idDcmn);
|
||||||
|
var idUsng = charIDToTypeID("Usng");
|
||||||
|
var ref535 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref535.putEnumerated(idChnl, idOrdn, idTrgt);
|
||||||
|
desc642.putReference(idUsng, ref535);
|
||||||
|
executeAction(idMk, desc642, DialogModes.NO);
|
||||||
|
|
||||||
|
newDoc = activeDocument
|
||||||
|
// =======================================================
|
||||||
|
activeDocument.resizeImage("200%", "200%", undefined, ResampleMethod.NEARESTNEIGHBOR)
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc934 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref535 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref535.putProperty(idChnl, idfsel);
|
||||||
|
desc934.putReference(idnull, ref535);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var ref536 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref536.putEnumerated(idChnl, idOrdn, idTrgt);
|
||||||
|
desc934.putReference(idT, ref536);
|
||||||
|
executeAction(idsetd, desc934, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
var idMk = charIDToTypeID("Mk ");
|
||||||
|
var desc403 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref288 = new ActionReference();
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
ref288.putClass(idPath);
|
||||||
|
desc403.putReference(idnull, ref288);
|
||||||
|
var idFrom = charIDToTypeID("From");
|
||||||
|
var ref289 = new ActionReference();
|
||||||
|
var idcsel = charIDToTypeID("csel");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref289.putProperty(idcsel, idfsel);
|
||||||
|
desc403.putReference(idFrom, ref289);
|
||||||
|
var idTlrn = charIDToTypeID("Tlrn");
|
||||||
|
var idPxl = charIDToTypeID("#Pxl");
|
||||||
|
desc403.putUnitDouble(idTlrn, idPxl, 0.500000);
|
||||||
|
executeAction(idMk, desc403, DialogModes.NO);
|
||||||
|
|
||||||
|
var subPathsLength = activeDocument.pathItems[0].subPathItems.length
|
||||||
|
|
||||||
|
if (subPathsLength>confirmThreshold){
|
||||||
|
var answer = confirm("基于"+subPathsLength+ "个拆分对象将创建图层. 你想继续吗?",true)
|
||||||
|
if (answer === false){
|
||||||
|
newDoc.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
activeDocument.quickMaskMode = false;
|
||||||
|
activeDocument.selection.deselect();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
activeDocument.resizeImage("50%", "50%", undefined, ResampleMethod.NEARESTNEIGHBOR)
|
||||||
|
|
||||||
|
var pathInfo = collectPathInfoFromDesc(activeDocument, activeDocument.pathItems[activeDocument.pathItems.length - 1])
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
newDoc.close(SaveOptions.DONOTSAVECHANGES)
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
activeDocument.quickMaskMode = false
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
//make channel
|
||||||
|
// =======================================================
|
||||||
|
var idMk = charIDToTypeID("Mk ");
|
||||||
|
var desc6 = new ActionDescriptor();
|
||||||
|
var idNw = charIDToTypeID("Nw ");
|
||||||
|
var desc7 = new ActionDescriptor();
|
||||||
|
var idNm = charIDToTypeID("Nm ");
|
||||||
|
desc7.putString(idNm, "ContiguityMask");
|
||||||
|
var idClrI = charIDToTypeID("ClrI");
|
||||||
|
var idMskI = charIDToTypeID("MskI");
|
||||||
|
var idMskA = charIDToTypeID("MskA");
|
||||||
|
desc7.putEnumerated(idClrI, idMskI, idMskA);
|
||||||
|
var idClr = charIDToTypeID("Clr ");
|
||||||
|
var desc8 = new ActionDescriptor();
|
||||||
|
var idRd = charIDToTypeID("Rd ");
|
||||||
|
desc8.putDouble(idRd, 255.000000);
|
||||||
|
var idGrn = charIDToTypeID("Grn ");
|
||||||
|
desc8.putDouble(idGrn, 0.000000);
|
||||||
|
var idBl = charIDToTypeID("Bl ");
|
||||||
|
desc8.putDouble(idBl, 0.000000);
|
||||||
|
var idRGBC = charIDToTypeID("RGBC");
|
||||||
|
desc7.putObject(idClr, idRGBC, desc8);
|
||||||
|
var idOpct = charIDToTypeID("Opct");
|
||||||
|
desc7.putInteger(idOpct, 50);
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
desc6.putObject(idNw, idChnl, desc7);
|
||||||
|
var idUsng = charIDToTypeID("Usng");
|
||||||
|
var ref5 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref5.putProperty(idChnl, idfsel);
|
||||||
|
desc6.putReference(idUsng, ref5);
|
||||||
|
executeAction(idMk, desc6, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
var layerCount = 1
|
||||||
|
for (i = 0; i < subPathsLength; i++) {
|
||||||
|
//deselect
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc279 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref137 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref137.putProperty(idChnl, idfsel);
|
||||||
|
desc279.putReference(idnull, ref137);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idNone = charIDToTypeID("None");
|
||||||
|
desc279.putEnumerated(idT, idOrdn, idNone);
|
||||||
|
executeAction(idsetd, desc279, DialogModes.NO);
|
||||||
|
///select alpha channel
|
||||||
|
var idslct = charIDToTypeID("slct");
|
||||||
|
var desc315 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref175 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
ref175.putName(idChnl, "ContiguityMask");
|
||||||
|
desc315.putReference(idnull, ref175);
|
||||||
|
executeAction(idslct, desc315, DialogModes.NO);
|
||||||
|
//use magic wand
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc263 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref123 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref123.putProperty(idChnl, idfsel);
|
||||||
|
desc263.putReference(idnull, ref123);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var desc264 = new ActionDescriptor();
|
||||||
|
var idHrzn = charIDToTypeID("Hrzn");
|
||||||
|
var idRlt = charIDToTypeID("#Rlt");
|
||||||
|
desc264.putUnitDouble(idHrzn, idRlt, pathInfo[i][0][0]);
|
||||||
|
var idVrtc = charIDToTypeID("Vrtc");
|
||||||
|
var idRlt = charIDToTypeID("#Rlt");
|
||||||
|
|
||||||
|
desc264.putUnitDouble(idVrtc, idRlt, pathInfo[i][0][1]);
|
||||||
|
var idPnt = charIDToTypeID("Pnt ");
|
||||||
|
desc263.putObject(idT, idPnt, desc264);
|
||||||
|
var idTlrn = charIDToTypeID("Tlrn");
|
||||||
|
desc263.putInteger(idTlrn, 1);
|
||||||
|
executeAction(idsetd, desc263, DialogModes.NO);
|
||||||
|
|
||||||
|
var idslct = charIDToTypeID("slct");
|
||||||
|
var desc346 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref205 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idRGB = charIDToTypeID("RGB ");
|
||||||
|
ref205.putEnumerated(idChnl, idChnl, idRGB);
|
||||||
|
desc346.putReference(idnull, ref205);
|
||||||
|
var idMkVs = charIDToTypeID("MkVs");
|
||||||
|
desc346.putBoolean(idMkVs, false);
|
||||||
|
executeAction(idslct, desc346, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
// =======================================================
|
||||||
|
var idCpTL = charIDToTypeID("CpTL");
|
||||||
|
executeAction(idCpTL, undefined, DialogModes.NO);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var idrasterizeLayer = stringIDToTypeID("rasterizeLayer");
|
||||||
|
var desc924 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref721 = new ActionReference();
|
||||||
|
var idLyr = charIDToTypeID("Lyr ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref721.putEnumerated(idLyr, idOrdn, idTrgt);
|
||||||
|
desc924.putReference(idnull, ref721);
|
||||||
|
var idWhat = charIDToTypeID("What");
|
||||||
|
var idrasterizeItem = stringIDToTypeID("rasterizeItem");
|
||||||
|
var idvectorMask = stringIDToTypeID("vectorMask");
|
||||||
|
desc924.putEnumerated(idWhat, idrasterizeItem, idvectorMask);
|
||||||
|
executeAction(idrasterizeLayer, desc924, DialogModes.NO);
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var idIntr = charIDToTypeID("Intr");
|
||||||
|
var desc864 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref658 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref658.putEnumerated(idChnl, idOrdn, idTrgt);
|
||||||
|
desc864.putReference(idnull, ref658);
|
||||||
|
var idWith = charIDToTypeID("With");
|
||||||
|
var ref659 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref659.putProperty(idChnl, idfsel);
|
||||||
|
desc864.putReference(idWith, ref659);
|
||||||
|
executeAction(idIntr, desc864, DialogModes.NO);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idDlt = charIDToTypeID("Dlt ");
|
||||||
|
var desc865 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref660 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref660.putEnumerated(idChnl, idOrdn, idTrgt);
|
||||||
|
desc865.putReference(idnull, ref660);
|
||||||
|
executeAction(idDlt, desc865, DialogModes.NO);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idMk = charIDToTypeID("Mk ");
|
||||||
|
var desc866 = new ActionDescriptor();
|
||||||
|
var idNw = charIDToTypeID("Nw ");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
desc866.putClass(idNw, idChnl);
|
||||||
|
var idAt = charIDToTypeID("At ");
|
||||||
|
var ref661 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idMsk = charIDToTypeID("Msk ");
|
||||||
|
ref661.putEnumerated(idChnl, idChnl, idMsk);
|
||||||
|
desc866.putReference(idAt, ref661);
|
||||||
|
var idUsng = charIDToTypeID("Usng");
|
||||||
|
var idUsrM = charIDToTypeID("UsrM");
|
||||||
|
var idRvlS = charIDToTypeID("RvlS");
|
||||||
|
desc866.putEnumerated(idUsng, idUsrM, idRvlS);
|
||||||
|
executeAction(idMk, desc866, DialogModes.NO);
|
||||||
|
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
var finalSuffix=suffix;
|
||||||
|
if (addCount===true)finalSuffix += layerCount;
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer.name = layerName + finalSuffix;
|
||||||
|
layerCount++;
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer=baseLayer;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc1045 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref578 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref578.putProperty(idChnl, idfsel);
|
||||||
|
desc1045.putReference(idnull, ref578);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idNone = charIDToTypeID("None");
|
||||||
|
desc1045.putEnumerated(idT, idOrdn, idNone);
|
||||||
|
executeAction(idsetd, desc1045, DialogModes.NO);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idDlt = charIDToTypeID("Dlt ");
|
||||||
|
var desc694 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref323 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
ref323.putName(idChnl, "ContiguityMask");
|
||||||
|
desc694.putReference(idnull, ref323);
|
||||||
|
executeAction(idDlt, desc694, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer.remove();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var idHd = charIDToTypeID("Hd ");
|
||||||
|
var desc736 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var list22 = new ActionList();
|
||||||
|
var ref541 = new ActionReference();
|
||||||
|
var idLyr = charIDToTypeID("Lyr ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref541.putEnumerated(idLyr, idOrdn, idTrgt);
|
||||||
|
list22.putReference(ref541);
|
||||||
|
desc736.putList(idnull, list22);
|
||||||
|
executeAction(idHd, desc736, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// pfaffenbichler and xbytor //
|
||||||
|
// at ps-scripts.com //
|
||||||
|
// created this function //
|
||||||
|
function collectPathInfoFromDesc(myDocument, thePath) {
|
||||||
|
var myDocument = app.activeDocument;
|
||||||
|
|
||||||
|
// based of functions from xbytor’s stdlib;
|
||||||
|
var ref = new ActionReference();
|
||||||
|
for (var l = 0; l < myDocument.pathItems.length; l++) {
|
||||||
|
var thisPath = myDocument.pathItems[l];
|
||||||
|
if (thisPath == thePath && thisPath.name == "Work Path") {
|
||||||
|
ref.putProperty(cTID("Path"), cTID("WrPt"));
|
||||||
|
};
|
||||||
|
if (thisPath == thePath && thisPath.name != "Work Path" && thisPath.kind != PathKind.VECTORMASK) {
|
||||||
|
ref.putIndex(cTID("Path"), l + 1);
|
||||||
|
};
|
||||||
|
if (thisPath == thePath && thisPath.kind == PathKind.VECTORMASK) {
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
var idvectorMask = stringIDToTypeID("vectorMask");
|
||||||
|
ref.putEnumerated(idPath, idPath, idvectorMask);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
var desc = app.executeActionGet(ref);
|
||||||
|
var pname = desc.getString(cTID('PthN'));
|
||||||
|
// create new array;
|
||||||
|
var theArray = new Array;
|
||||||
|
var pathComponents = desc.getObjectValue(cTID("PthC")).getList(sTID('pathComponents'));
|
||||||
|
// for subpathitems;
|
||||||
|
for (var m = 0; m < pathComponents.count; m++) {
|
||||||
|
var listKey = pathComponents.getObjectValue(m).getList(sTID("subpathListKey"));
|
||||||
|
// for subpathitem’s count;
|
||||||
|
for (var n = 0; n < listKey.count; n++) {
|
||||||
|
var points = listKey.getObjectValue(n).getList(sTID('points'));
|
||||||
|
// get first point;
|
||||||
|
var anchorObj = points.getObjectValue(0).getObjectValue(sTID("anchor"));
|
||||||
|
var anchor = [anchorObj.getUnitDoubleValue(sTID('horizontal')), anchorObj.getUnitDoubleValue(sTID('vertical'))];
|
||||||
|
var thisPoint = [anchor];
|
||||||
|
theArray.push(thisPoint);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// by xbytor, thanks to him;
|
||||||
|
|
||||||
|
|
||||||
|
function cTID(s) {
|
||||||
|
return cTID[s] || cTID[s] = app.charIDToTypeID(s);
|
||||||
|
};
|
||||||
|
|
||||||
|
function sTID(s) {
|
||||||
|
return sTID[s] || sTID[s] = app.stringIDToTypeID(s);
|
||||||
|
};
|
||||||
|
// reset;
|
||||||
|
return theArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function makePreviewSelection(){
|
||||||
|
makeSelection()
|
||||||
|
app.refresh()
|
||||||
|
activeDocument.quickMaskMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSelection(){
|
||||||
|
try{
|
||||||
|
|
||||||
|
var idsetd = charIDToTypeID("setd");
|
||||||
|
var desc922 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref529 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref529.putProperty(idChnl, idfsel);
|
||||||
|
desc922.putReference(idnull, ref529);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var ref530 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idTrsp = charIDToTypeID("Trsp");
|
||||||
|
ref530.putEnumerated(idChnl, idChnl, idTrsp);
|
||||||
|
desc922.putReference(idT, ref530);
|
||||||
|
executeAction(idsetd, desc922, DialogModes.NO);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
var idIntr = charIDToTypeID("Intr");
|
||||||
|
var desc846 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref639 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idMsk = charIDToTypeID("Msk ");
|
||||||
|
ref639.putEnumerated(idChnl, idChnl, idMsk);
|
||||||
|
desc846.putReference(idnull, ref639);
|
||||||
|
var idWith = charIDToTypeID("With");
|
||||||
|
var ref640 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref640.putProperty(idChnl, idfsel);
|
||||||
|
desc846.putReference(idWith, ref640);
|
||||||
|
executeAction(idIntr, desc846, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// =======================================================
|
||||||
|
var idIntW = charIDToTypeID("IntW");
|
||||||
|
var desc787 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID("null");
|
||||||
|
var ref572 = new ActionReference();
|
||||||
|
var idChnl = charIDToTypeID("Chnl");
|
||||||
|
var idfsel = charIDToTypeID("fsel");
|
||||||
|
ref572.putProperty(idChnl, idfsel);
|
||||||
|
desc787.putReference(idnull, ref572);
|
||||||
|
var idT = charIDToTypeID("T ");
|
||||||
|
var ref573 = new ActionReference();
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
var idPath = charIDToTypeID("Path");
|
||||||
|
var idvectorMask = stringIDToTypeID("vectorMask");
|
||||||
|
ref573.putEnumerated(idPath, idPath, idvectorMask);
|
||||||
|
var idLyr = charIDToTypeID("Lyr ");
|
||||||
|
var idOrdn = charIDToTypeID("Ordn");
|
||||||
|
var idTrgt = charIDToTypeID("Trgt");
|
||||||
|
ref573.putEnumerated(idLyr, idOrdn, idTrgt);
|
||||||
|
desc787.putReference(idT, ref573);
|
||||||
|
var idVrsn = charIDToTypeID("Vrsn");
|
||||||
|
desc787.putInteger(idVrsn, 1);
|
||||||
|
var idvectorMaskParams = stringIDToTypeID("vectorMaskParams");
|
||||||
|
desc787.putBoolean(idvectorMaskParams, true);
|
||||||
|
executeAction(idIntW, desc787, DialogModes.NO);
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (tolerance >= 2) {
|
||||||
|
|
||||||
|
activeDocument.selection.expand(Math.floor(tolerance / 2))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.quickMaskMode = true;
|
||||||
|
|
||||||
|
|
||||||
|
var idThrs = charIDToTypeID("Thrs");
|
||||||
|
var desc479 = new ActionDescriptor();
|
||||||
|
var idLvl = charIDToTypeID("Lvl ");
|
||||||
|
desc479.putInteger(idLvl, 1);
|
||||||
|
executeAction(idThrs, desc479, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (tolerance % 2 == 1) {
|
||||||
|
|
||||||
|
var idMtnB = charIDToTypeID("MtnB");
|
||||||
|
var desc213 = new ActionDescriptor();
|
||||||
|
var idAngl = charIDToTypeID("Angl");
|
||||||
|
desc213.putInteger(idAngl, 0);
|
||||||
|
var idDstn = charIDToTypeID("Dstn");
|
||||||
|
var idPxl = charIDToTypeID("#Pxl");
|
||||||
|
desc213.putUnitDouble(idDstn, idPxl, 1.000000);
|
||||||
|
executeAction(idMtnB, desc213, DialogModes.NO);
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idMtnB = charIDToTypeID("MtnB");
|
||||||
|
var desc214 = new ActionDescriptor();
|
||||||
|
var idAngl = charIDToTypeID("Angl");
|
||||||
|
desc214.putInteger(idAngl, 90);
|
||||||
|
var idDstn = charIDToTypeID("Dstn");
|
||||||
|
var idPxl = charIDToTypeID("#Pxl");
|
||||||
|
desc214.putUnitDouble(idDstn, idPxl, 1.000000);
|
||||||
|
executeAction(idMtnB, desc214, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
// =======================================================
|
||||||
|
var idThrs = charIDToTypeID("Thrs");
|
||||||
|
var desc215 = new ActionDescriptor();
|
||||||
|
var idLvl = charIDToTypeID("Lvl ");
|
||||||
|
desc215.putInteger(idLvl, 1);
|
||||||
|
executeAction(idThrs, desc215, DialogModes.NO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDialog(){
|
||||||
|
|
||||||
|
var dlg = new Window('dialog', 'PNG素材拆分图层');
|
||||||
|
dlg.alignChildren ='left';
|
||||||
|
|
||||||
|
dlg.gap = dlg.add('group')
|
||||||
|
dlg.gap.orientation= 'row';
|
||||||
|
dlg.gap.txt=dlg.gap.add('statictext', undefined,'间隙大于多少时拆分?');
|
||||||
|
dlg.gap.input=dlg.gap.add('edittext', undefined,tolerance);
|
||||||
|
dlg.gap.input.preferredSize = [20,20];
|
||||||
|
dlg.gap.txt2=dlg.gap.add('statictext', undefined,'像素');
|
||||||
|
dlg.gap.btnPreview= dlg.gap.add('button', undefined,'蒙版预览');
|
||||||
|
dlg.gap.btnPreview.preferredSize = [55,20]
|
||||||
|
|
||||||
|
dlg.naming = dlg.add('panel',undefined,'图层重命名')
|
||||||
|
dlg.naming.alignChildren ='left';
|
||||||
|
dlg.naming.suffix = dlg.naming.add('group')
|
||||||
|
dlg.naming.suffix.orientation= 'row';
|
||||||
|
dlg.naming.suffix.txt=dlg.naming.suffix.add('statictext', undefined,'后缀:');
|
||||||
|
dlg.naming.suffix.input=dlg.naming.suffix.add('edittext', undefined,suffix);
|
||||||
|
dlg.naming.suffix.input.preferredSize = [60,20];
|
||||||
|
|
||||||
|
dlg.naming.suffix.chkbox = dlg.naming.suffix.add('checkbox', undefined, '添加序号')
|
||||||
|
dlg.naming.suffix.chkbox.value=addCount;
|
||||||
|
|
||||||
|
dlg.naming.txtPreview = dlg.naming.add('statictext', undefined, layerNamePreview)
|
||||||
|
dlg.naming.txtPreview.preferredSize = [200,20];
|
||||||
|
|
||||||
|
dlg.btnPnl= dlg.add('group');
|
||||||
|
dlg.btnPnl.alignment ='right';
|
||||||
|
dlg.btnPnl.okBtn = dlg.btnPnl.add('button', undefined, '确定', {name:'ok'});
|
||||||
|
dlg.btnPnl.okBtn.active=true;
|
||||||
|
dlg.btnPnl.cancelBtn = dlg.btnPnl.add('button', undefined, '取消', {name:'cancel'});
|
||||||
|
|
||||||
|
dlg.naming.suffix.input.onChanging= function(){
|
||||||
|
layerNamePreview=activeDocument.activeLayer.name + dlg.naming.suffix.input.text
|
||||||
|
if (dlg.naming.suffix.chkbox.value === true){
|
||||||
|
layerNamePreview += "1"
|
||||||
|
}
|
||||||
|
dlg.naming.txtPreview.text =layerNamePreview
|
||||||
|
}
|
||||||
|
dlg.naming.suffix.chkbox.onClick = function(){
|
||||||
|
layerNamePreview=activeDocument.activeLayer.name + dlg.naming.suffix.input.text
|
||||||
|
if (dlg.naming.suffix.chkbox.value === true){
|
||||||
|
layerNamePreview += "1"
|
||||||
|
}
|
||||||
|
dlg.naming.txtPreview.text = layerNamePreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dlg.gap.input.onChanging = function() {
|
||||||
|
if (parseInt(dlg.gap.input.text) == 1){
|
||||||
|
dlg.gap.txt2.text = '像素'
|
||||||
|
}else{
|
||||||
|
dlg.gap.txt2.text = '像素'
|
||||||
|
}
|
||||||
|
tolerance = parseInt (dlg.gap.input.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
dlg.gap.btnPreview.onClick = function() {
|
||||||
|
makePreviewSelection()
|
||||||
|
}
|
||||||
|
|
||||||
|
x=dlg.show();
|
||||||
|
|
||||||
|
tolerance = parseInt (dlg.gap.input.text)
|
||||||
|
suffix = dlg.naming.suffix.input.text
|
||||||
|
addCount=dlg.naming.suffix.chkbox.value
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
dxf22_jscode = """
|
|
||||||
|
|
||||||
|
|
||||||
//PS智能对象换图
|
//PS智能对象换图
|
||||||
|
|
||||||
function 模特换衣功能(){
|
function 模特换衣功能(){
|
||||||
@@ -104,6 +102,10 @@ var button5 = group4.add("button");
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//~ sPath = decodeURI(File($.fileName).path);
|
||||||
|
//~ edittext1.text = sPath + "/模板/笨鸟跨境 圣诞袜模板 单边 加大-38.psd";
|
||||||
|
//~ edittext2.text = sPath + "/替换图片子文件夹";
|
||||||
|
//~ edittext3.text = sPath + "/导出目录";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -199,11 +201,11 @@ function main(模板路径,素材目录,导出目录)
|
|||||||
|
|
||||||
if(cbox2.value) //保持结构
|
if(cbox2.value) //保持结构
|
||||||
{
|
{
|
||||||
结构导出目录 = 导出目录+"/" + getRelativePath(decodeURI(File(scpsd_path).path), 素材目录);
|
结构导出目录 = 导出目录+"\\" + getRelativePath(decodeURI(File(scpsd_path).path), 素材目录);
|
||||||
|
|
||||||
if(Folder(结构导出目录).exists==false){Folder(结构导出目录).create();}
|
if(Folder(结构导出目录).exists==false){Folder(结构导出目录).create();}
|
||||||
|
|
||||||
保存路径 = 结构导出目录+"/"+素材名+".jpg";
|
保存路径 = 结构导出目录+"\\"+素材名+".jpg";
|
||||||
$.writeln(保存路径);
|
$.writeln(保存路径);
|
||||||
|
|
||||||
if(cbox3.value) //按切片
|
if(cbox3.value) //按切片
|
||||||
@@ -225,7 +227,7 @@ function main(模板路径,素材目录,导出目录)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
保存JPG(导出目录+"/"+素材名+".jpg");
|
保存JPG(导出目录+"\\"+素材名+".jpg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -449,5 +451,3 @@ function 按切片导出图片(保存路径,替换词)
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
|
||||||
340
PSMARK代码块/SO样打印拼贴加字.jsx
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
|
||||||
|
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
dialog.text = "打印拼贴软件 使用前请看规则";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "S/O样自动连晒";
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "宽高设定(cm):";
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "row";
|
||||||
|
group2.alignChildren = ["left","center"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var statictext2 = group2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "宽度";
|
||||||
|
|
||||||
|
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||||
|
|
||||||
|
edittext1.preferredSize.width = 60;
|
||||||
|
|
||||||
|
var statictext3 = group2.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "宽度";
|
||||||
|
|
||||||
|
var edittext2 = group2.add('edittext {properties: {name: "edittext2"}}');
|
||||||
|
|
||||||
|
edittext2.preferredSize.width = 60;
|
||||||
|
|
||||||
|
// GROUP3
|
||||||
|
// ======
|
||||||
|
var group3 = panel1.add("group", undefined, {name: "group3"});
|
||||||
|
group3.orientation = "row";
|
||||||
|
group3.alignChildren = ["left","center"];
|
||||||
|
group3.spacing = 10;
|
||||||
|
group3.margins = 0;
|
||||||
|
|
||||||
|
var statictext4 = group3.add("statictext", undefined, undefined, {name: "statictext4"});
|
||||||
|
statictext4.text = "字体属性:";
|
||||||
|
|
||||||
|
var dropdown1_array = ["文件名"];
|
||||||
|
var dropdown1 = group3.add("dropdownlist", undefined, undefined, {name: "dropdown1", items: dropdown1_array});
|
||||||
|
dropdown1.selection = 0;
|
||||||
|
dropdown1.preferredSize.width = 100;
|
||||||
|
|
||||||
|
// GROUP4
|
||||||
|
// ======
|
||||||
|
var group4 = panel1.add("group", undefined, {name: "group4"});
|
||||||
|
group4.orientation = "row";
|
||||||
|
group4.alignChildren = ["left","center"];
|
||||||
|
group4.spacing = 10;
|
||||||
|
group4.margins = 0;
|
||||||
|
|
||||||
|
var statictext5 = group4.add("statictext", undefined, undefined, {name: "statictext5"});
|
||||||
|
statictext5.text = "字体方向:";
|
||||||
|
|
||||||
|
var dropdown2_array = ["以下对齐"];
|
||||||
|
var dropdown2 = group4.add("dropdownlist", undefined, undefined, {name: "dropdown2", items: dropdown2_array});
|
||||||
|
dropdown2.selection = 0;
|
||||||
|
dropdown2.preferredSize.width = 100;
|
||||||
|
|
||||||
|
// GROUP5
|
||||||
|
// ======
|
||||||
|
var group5 = panel1.add("group", undefined, {name: "group5"});
|
||||||
|
group5.orientation = "row";
|
||||||
|
group5.alignChildren = ["left","center"];
|
||||||
|
group5.spacing = 10;
|
||||||
|
group5.margins = 0;
|
||||||
|
|
||||||
|
var statictext6 = group5.add("statictext", undefined, undefined, {name: "statictext6"});
|
||||||
|
statictext6.text = "字体大小:";
|
||||||
|
|
||||||
|
var edittext3 = group5.add('edittext {properties: {name: "edittext3"}}');
|
||||||
|
edittext3.text ="60"
|
||||||
|
edittext3.preferredSize.width = 60;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
|
||||||
|
var group6 = panel1.add("group", undefined, {name: "group6"});
|
||||||
|
group6.orientation = "row";
|
||||||
|
group6.alignChildren = ["left","center"];
|
||||||
|
group6.spacing = 10;
|
||||||
|
group6.margins = 0;
|
||||||
|
|
||||||
|
var statictext7 = group6.add("statictext", undefined, undefined, {name: "statictext7"});
|
||||||
|
statictext7.text = "文件路径:";
|
||||||
|
|
||||||
|
var button1 = group6.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "输出文件夹目录";
|
||||||
|
button1.preferredSize.width = 100;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var progressbar1 = group1.add("progressbar", undefined, undefined, {name: "progressbar1"});
|
||||||
|
progressbar1.maxvalue = 100;
|
||||||
|
progressbar1.value = 50;
|
||||||
|
progressbar1.preferredSize.width = 50;
|
||||||
|
progressbar1.preferredSize.height = 4;
|
||||||
|
|
||||||
|
// GROUP6
|
||||||
|
// ======
|
||||||
|
var group6 = dialog.add("group", undefined, {name: "group6"});
|
||||||
|
group6.orientation = "column";
|
||||||
|
group6.alignChildren = ["fill","top"];
|
||||||
|
group6.spacing = 10;
|
||||||
|
group6.margins = 0;
|
||||||
|
|
||||||
|
var ok = group6.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "执行";
|
||||||
|
ok.alignment = ["center","top"];
|
||||||
|
|
||||||
|
var cancel = group6.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
cancel.alignment = ["fill","top"];
|
||||||
|
|
||||||
|
var button3 = group6.add("button", undefined, undefined, {name: "button3"});
|
||||||
|
button3.text = "关于";
|
||||||
|
button3.alignment = ["right","top"];
|
||||||
|
|
||||||
|
var button4 = group6.add("button", undefined, undefined, {name: "button4"});
|
||||||
|
button4.text = "规则";
|
||||||
|
button4.alignment = ["right","top"];
|
||||||
|
|
||||||
|
button1.onClick=function(){
|
||||||
|
|
||||||
|
button1.text =Folder.selectDialog ("输出文件夹目录").fsName;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button3.onClick = function () {
|
||||||
|
|
||||||
|
alert("by jimi 2022.8.9 裁片自动排版软件18620223577",dialog.text+"----关于");
|
||||||
|
}
|
||||||
|
|
||||||
|
button4.onClick = function () {
|
||||||
|
|
||||||
|
alert("须在D盘建立一个名为*输入*的文件夹",dialog.text+"----使用须知");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//处理函数
|
||||||
|
ok .onClick=function(){
|
||||||
|
|
||||||
|
//~ //~ if (myEditText!="") {
|
||||||
|
var myFolder=new Folder(button1.text)
|
||||||
|
//打开文件夹中的每一个文件开始处理
|
||||||
|
|
||||||
|
var myFiles=myFolder.getFiles ("*.tif*")
|
||||||
|
|
||||||
|
|
||||||
|
for(i=0 ;i< myFiles.length;i++){
|
||||||
|
//开始处理每一个文件
|
||||||
|
|
||||||
|
//断判是文件格式
|
||||||
|
if(myFiles[i] instanceof File)
|
||||||
|
app.open(myFiles[i])
|
||||||
|
|
||||||
|
|
||||||
|
var document =app.activeDocument
|
||||||
|
//合并图层
|
||||||
|
document.flatten()
|
||||||
|
|
||||||
|
|
||||||
|
//预设图案
|
||||||
|
|
||||||
|
var 当前文档名称=activeDocument.name
|
||||||
|
|
||||||
|
|
||||||
|
var idMk = charIDToTypeID( "Mk " );
|
||||||
|
var desc14 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID( "null" );
|
||||||
|
var ref3 = new ActionReference();
|
||||||
|
var idPtrn = charIDToTypeID( "Ptrn" );
|
||||||
|
ref3.putClass( idPtrn );
|
||||||
|
desc14.putReference( idnull, ref3 );
|
||||||
|
var idUsng = charIDToTypeID( "Usng" );
|
||||||
|
var ref4 = new ActionReference();
|
||||||
|
var idPrpr = charIDToTypeID( "Prpr" );
|
||||||
|
var idfsel = charIDToTypeID( "fsel" );
|
||||||
|
ref4.putProperty( idPrpr, idfsel );
|
||||||
|
var idDcmn = charIDToTypeID( "Dcmn" );
|
||||||
|
var idOrdn = charIDToTypeID( "Ordn" );
|
||||||
|
var idTrgt = charIDToTypeID( "Trgt" );
|
||||||
|
ref4.putEnumerated( idDcmn, idOrdn, idTrgt );
|
||||||
|
desc14.putReference( idUsng, ref4 );
|
||||||
|
var idNm = charIDToTypeID( "Nm " );
|
||||||
|
desc14.putString( idNm, """当前文档名称""" );
|
||||||
|
executeAction( idMk, desc14, DialogModes.NO );
|
||||||
|
//
|
||||||
|
|
||||||
|
//重设宽高
|
||||||
|
//记录宽高以调整宽高
|
||||||
|
//-.../-.--/-.-./..-./..../-.--
|
||||||
|
var sWidth=Number (edittext1.text); //宽;
|
||||||
|
var sHeight=Number (edittext2.text); //宽;
|
||||||
|
|
||||||
|
var fontsz=Number (edittext3.text); //宽;
|
||||||
|
//将图片裁切成指定宽高
|
||||||
|
app.activeDocument.crop([UnitValue("0px"),UnitValue("0px"),UnitValue(sWidth,"cm"),UnitValue(sHeight,"cm")]);
|
||||||
|
|
||||||
|
|
||||||
|
//填充当前文档图案
|
||||||
|
var idMk = charIDToTypeID( "Mk " );
|
||||||
|
var desc111 = new ActionDescriptor();
|
||||||
|
var idnull = charIDToTypeID( "null" );
|
||||||
|
var ref20 = new ActionReference();
|
||||||
|
var idcontentLayer = stringIDToTypeID( "contentLayer" );
|
||||||
|
ref20.putClass( idcontentLayer );
|
||||||
|
desc111.putReference( idnull, ref20 );
|
||||||
|
var idUsng = charIDToTypeID( "Usng" );
|
||||||
|
var desc112 = new ActionDescriptor();
|
||||||
|
var idType = charIDToTypeID( "Type" );
|
||||||
|
var desc113 = new ActionDescriptor();
|
||||||
|
var idPtrn = charIDToTypeID( "Ptrn" );
|
||||||
|
var desc114 = new ActionDescriptor();
|
||||||
|
var idNm = charIDToTypeID( "Nm " );
|
||||||
|
desc114.putString( idNm, """当前文档名称""" );
|
||||||
|
var idIdnt = charIDToTypeID( "Idnt" );
|
||||||
|
desc114.putString( idIdnt, """当前文档名称""" );
|
||||||
|
var idPtrn = charIDToTypeID( "Ptrn" );
|
||||||
|
desc113.putObject( idPtrn, idPtrn, desc114 );
|
||||||
|
var idpatternLayer = stringIDToTypeID( "patternLayer" );
|
||||||
|
desc112.putObject( idType, idpatternLayer, desc113 );
|
||||||
|
var idcontentLayer = stringIDToTypeID( "contentLayer" );
|
||||||
|
desc111.putObject( idUsng, idcontentLayer, desc112 );
|
||||||
|
executeAction( idMk, desc111, DialogModes.NO );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//记录字体大小并修改
|
||||||
|
//r textsz= Number(dialog.group5.statictext6.edittext3.text); //宽;
|
||||||
|
|
||||||
|
//新建图层
|
||||||
|
activeDocument.artLayers.add()
|
||||||
|
//将新建图层变成文本图层
|
||||||
|
activeDocument.activeLayer.kind=LayerKind.TEXT
|
||||||
|
//将文本内容改为当前文档name
|
||||||
|
activeDocument.activeLayer.textItem.contents=activeDocument.name
|
||||||
|
//字体大小
|
||||||
|
activeDocument.activeLayer.textItem.size= fontsz
|
||||||
|
//文字字体
|
||||||
|
activeDocument.activeLayer.textItem.font="微软雅黑"
|
||||||
|
//文字层边界
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
x=activeDocument.width-activeDocument.activeLayer.bounds[2]
|
||||||
|
//文字层边界
|
||||||
|
y=activeDocument.activeLayer.bounds[1]
|
||||||
|
//对齐
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer.translate(x,-y)
|
||||||
|
//偏移
|
||||||
|
activeDocument.activeLayer.translate(UnitValue("-1cm"),UnitValue("+0.5cm"))
|
||||||
|
|
||||||
|
|
||||||
|
//电白
|
||||||
|
var myCopyLayer=activeDocument.activeLayer.duplicate (activeDocument.activeLayer,ElementPlacement.PLACEAFTER)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
myCopyLayer.rasterize(RasterizeType.ENTIRELAYER)
|
||||||
|
|
||||||
|
//T填充颜色参数
|
||||||
|
var myColor=new SolidColor()
|
||||||
|
myColor.rgb.red=255
|
||||||
|
myColor.rgb.green=255
|
||||||
|
myColor.rgb.blue=255
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.activeLayer=myCopyLayer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fillType=myColor
|
||||||
|
|
||||||
|
mode=ColorBlendMode.NORMAL
|
||||||
|
oacity=100
|
||||||
|
preserveTransparency=true
|
||||||
|
|
||||||
|
activeDocument.selection.fill(fillType,mode,oacity,preserveTransparency)
|
||||||
|
|
||||||
|
//白边
|
||||||
|
activeDocument.activeLayer.applyMinimum(10)
|
||||||
|
|
||||||
|
|
||||||
|
document.flatten()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//保存输出
|
||||||
|
|
||||||
|
//r CSyFolder=File(button2.text)
|
||||||
|
|
||||||
|
saveIn=File("d:/输入/"+activeDocument.name);
|
||||||
|
tifSaveOpt = new TiffSaveOptions();
|
||||||
|
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||||
|
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||||
|
asCopy=true
|
||||||
|
app.activeDocument.saveAs(saveIn,tifSaveOpt,asCopy);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
10
PSMARK代码块/ok的代码模块.jsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
function 执行动作(actionName, actionSetName) {
|
||||||
|
try {
|
||||||
|
app.doAction(actionName, actionSetName);
|
||||||
|
} catch (e) {
|
||||||
|
alert("动作调用出错: " + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例:
|
||||||
|
执行动作("某个动作名称", "某个动作集名称");
|
||||||
11
PSMARK代码块/位移.jsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
function offset_47921752929688() //位移
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putInteger(stringIDToTypeID("horizontal"), 300);
|
||||||
|
d.putInteger(stringIDToTypeID("vertical"), 300);
|
||||||
|
d.putEnumerated(stringIDToTypeID("fill"), stringIDToTypeID("fillMode"), stringIDToTypeID("wrap"));
|
||||||
|
executeAction(stringIDToTypeID("offset"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
62
PSMARK代码块/保留历史记录.jsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
function 历史记录保留() {
|
||||||
|
|
||||||
|
app.activeDocument.suspendHistory("历史记录保留", "历史记录保留()");
|
||||||
|
}
|
||||||
|
|
||||||
|
function 历史记录保留() {
|
||||||
|
|
||||||
|
|
||||||
|
主文档 = app.activeDocument;
|
||||||
|
主文档名称 = 主文档.name;
|
||||||
|
|
||||||
|
// 遍历当前打开的文档
|
||||||
|
for (var i = 0; i < app.documents.length; i++) {
|
||||||
|
var document = app.documents[i];
|
||||||
|
var documentName = document.name;
|
||||||
|
|
||||||
|
// 判断文档名称是否与主文档名称不相同
|
||||||
|
if (documentName !== 主文档名称) {
|
||||||
|
// 设置当前文档为活动文档
|
||||||
|
app.activeDocument = document;
|
||||||
|
取消选择()
|
||||||
|
历史记录快照保留()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
app.activeDocument = 主文档
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 历史记录快照保留() //删除图层
|
||||||
|
{
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("snapshotClass"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("historyState"), stringIDToTypeID("currentHistoryState"));
|
||||||
|
d.putReference(stringIDToTypeID("from"), r1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 取消选择() //取消选择
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("to"), stringIDToTypeID("ordinal"), stringIDToTypeID("none"));
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
142
PSMARK代码块/关闭事件监听.jsx
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
|
||||||
|
function 关闭事件监听(文件路径) {
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
arg_num = arguments.length;
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
arguments = []; //初始赋值为空
|
||||||
|
}
|
||||||
|
|
||||||
|
eventName = "set";
|
||||||
|
|
||||||
|
//开启监听
|
||||||
|
//enable_notifier(eventName,文件路径);
|
||||||
|
//
|
||||||
|
//取消监听
|
||||||
|
disable_notifier(eventName,文件路径);
|
||||||
|
|
||||||
|
//这里进行监控调用事件
|
||||||
|
if (arguments.length >= 2)
|
||||||
|
{
|
||||||
|
//alert(arguments.length);
|
||||||
|
//~ alert(arguments[0],"动作参数1"); //动作描述符 AR
|
||||||
|
//~ alert(arguments[1],"动作参数2"); //动作事件ID
|
||||||
|
|
||||||
|
|
||||||
|
moveToGuideCenter();
|
||||||
|
//main(arguments[0], arguments[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveToGuideCenter() {
|
||||||
|
if (app.documents.length === 0 || !app.activeDocument.selection.bounds) {
|
||||||
|
alert("没有打开的文档或没有有效的选区。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
var guides = doc.guides;
|
||||||
|
var xGuide = null;
|
||||||
|
var yGuide = null;
|
||||||
|
|
||||||
|
// 找到 X 方向和 Y 方向上的参考线
|
||||||
|
for (var i = 0; i < guides.length; i++) {
|
||||||
|
var guide = guides[i];
|
||||||
|
if (guide.direction === Direction.HORIZONTAL && yGuide === null) {
|
||||||
|
yGuide = guide;
|
||||||
|
} else if (guide.direction === Direction.VERTICAL && xGuide === null) {
|
||||||
|
xGuide = guide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xGuide === null || yGuide === null) {
|
||||||
|
alert("必须确保存在一条水平和一条垂直参考线。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var intersectionX = xGuide.coordinate;
|
||||||
|
var intersectionY = yGuide.coordinate;
|
||||||
|
|
||||||
|
var bounds = doc.selection.bounds;
|
||||||
|
var xCenter = bounds[0] + (bounds[2] - bounds[0]) / 2;
|
||||||
|
var yCenter = bounds[1] + (bounds[3] - bounds[1]) / 2;
|
||||||
|
|
||||||
|
var deltaX = intersectionX - xCenter;
|
||||||
|
var deltaY = intersectionY - yCenter;
|
||||||
|
|
||||||
|
// 取消当前选区
|
||||||
|
doc.selection.deselect();
|
||||||
|
|
||||||
|
// 移动选区图层
|
||||||
|
var selectedLayer = doc.activeLayer;
|
||||||
|
selectedLayer.translate(deltaX, deltaY);
|
||||||
|
move_60275268554688()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function move_60275268554688() // һ
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function enable_notifier(event_name, script_name, event_class)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (var i = 0; i < app.notifiers.length; i++)
|
||||||
|
{
|
||||||
|
if (app.notifiers[i].event == event_name &&
|
||||||
|
File(app.notifiers[i].eventFile).fsName.toLowerCase() == File(script_name).fsName.toLowerCase())
|
||||||
|
{
|
||||||
|
if (!app.notifiersEnabled) app.notifiersEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.notifiers.add(event_name, File(script_name), event_class);
|
||||||
|
app.notifiersEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (e) { _alert(e); return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function disable_notifier(event_name, script_name, event_class)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ret = false;
|
||||||
|
|
||||||
|
for (var i = 0; i < app.notifiers.length; i++)
|
||||||
|
{
|
||||||
|
if (app.notifiers[i].event == event_name &&
|
||||||
|
File(app.notifiers[i].eventFile).fsName.toLowerCase() == File(script_name).fsName.toLowerCase())
|
||||||
|
{
|
||||||
|
app.notifiers[i].remove();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!app.notifiers.length) app.notifiersEnabled = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (e) { _alert(e); return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
37
PSMARK代码块/写入json文件.jsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
app.preferences.rulerUnits = Units.MM;
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
var 扩展毫米数 = 80;
|
||||||
|
|
||||||
|
// 获取文档中的所有图层
|
||||||
|
var allLayers = doc.layers;
|
||||||
|
|
||||||
|
// 创建一个数组来存储子图层的名称
|
||||||
|
var 子图层名称数组 = [];
|
||||||
|
|
||||||
|
// 循环遍历所有图层
|
||||||
|
for (var i = 0; i < allLayers.length; i++) {
|
||||||
|
// 检查图层是否是图层组
|
||||||
|
if (allLayers[i] instanceof LayerSet && allLayers[i].name === "填充底图") {
|
||||||
|
// 获取图层组中的所有子图层
|
||||||
|
var subLayers = allLayers[i].layers;
|
||||||
|
|
||||||
|
// 将子图层的名称添加到数组中,并在名称后面加上"-填充底图"
|
||||||
|
for (var j = 0; j < subLayers.length; j++) {
|
||||||
|
子图层名称数组.push('"' + subLayers[j].name + '-填充底图"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动创建JSON格式的字符串
|
||||||
|
var jsonStr = '[' + 子图层名称数组.join(', ') + ']';
|
||||||
|
|
||||||
|
// 创建一个文件对象指向桌面
|
||||||
|
var desktop = Folder.desktop;
|
||||||
|
var file = new File(desktop + "/子图层名称.json");
|
||||||
|
|
||||||
|
// 打开文件,写入JSON字符串,然后关闭文件
|
||||||
|
file.open('w');
|
||||||
|
file.write(jsonStr);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
alert("子图层名称已保存到桌面的JSON文件中!");
|
||||||
|
}
|
||||||
|
}
|
||||||
12
PSMARK代码块/切换主文档.jsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
function switchToDocument(docName) {
|
||||||
|
var docs = app.documents;
|
||||||
|
for (var i = 0; i < docs.length; i++) {
|
||||||
|
if (docs[i].name == docName) {
|
||||||
|
app.activeDocument = docs[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
switchToDocument("xxx.psd");
|
||||||
42
PSMARK代码块/删除图层.jsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
function 删除图层3()
|
||||||
|
{
|
||||||
|
app.activeDocument.suspendHistory("delete layer", "删除图层2()");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 删除图层2()
|
||||||
|
{
|
||||||
|
图层选择()
|
||||||
|
删除图层()
|
||||||
|
}
|
||||||
|
function 图层选择() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "图层 2");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(3);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
function 删除图层() //删除图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(3);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
31
PSMARK代码块/判断字体函数.jsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
function 缩小字体图层至文档一半() {
|
||||||
|
var 文档 = app.activeDocument;
|
||||||
|
var 字体图层 = null;
|
||||||
|
|
||||||
|
// 遍历文档中的图层以找到名为"字体"的图层
|
||||||
|
for (var i = 0; i < 文档.artLayers.length; i++) {
|
||||||
|
if (文档.artLayers[i].name === "字体") {
|
||||||
|
字体图层 = 文档.artLayers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (字体图层 !== null) {
|
||||||
|
// 获取文档的宽度的一半
|
||||||
|
var 目标宽度 = 文档.width / 2;
|
||||||
|
|
||||||
|
// 获取图层的当前宽度
|
||||||
|
var 图层宽度 = 字体图层.bounds[2] - 字体图层.bounds[0];
|
||||||
|
|
||||||
|
// 计算缩放比例
|
||||||
|
var 缩放比例 = 目标宽度 / 图层宽度 * 100;
|
||||||
|
|
||||||
|
// 缩放图层
|
||||||
|
字体图层.resize(缩放比例, 缩放比例, AnchorPosition.MIDDLECENTER);
|
||||||
|
} else {
|
||||||
|
alert("未找到名为'字体'的图层");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用函数
|
||||||
|
缩小字体图层至文档一半();
|
||||||
189
PSMARK代码块/加字函数.jsx
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
创建并处理文本图层();
|
||||||
|
|
||||||
|
|
||||||
|
function 创建并处理文本图层() {
|
||||||
|
// 新建图层
|
||||||
|
var textLayer = activeDocument.artLayers.add();
|
||||||
|
|
||||||
|
// 将新建图层变成文本图层
|
||||||
|
textLayer.kind = LayerKind.TEXT;
|
||||||
|
|
||||||
|
// 将文本内容改为当前文档name
|
||||||
|
textLayer.textItem.contents = activeDocument.name;
|
||||||
|
|
||||||
|
// 字体大小固定值
|
||||||
|
var 固定字体大小 = 30; // 例如,30像素
|
||||||
|
textLayer.textItem.size = 固定字体大小;
|
||||||
|
|
||||||
|
// 文字字体固定值
|
||||||
|
textLayer.textItem.font = "微软雅黑";
|
||||||
|
|
||||||
|
// 计算并调整文本位置
|
||||||
|
var x = activeDocument.width - textLayer.bounds[2];
|
||||||
|
var y = textLayer.bounds[1];
|
||||||
|
textLayer.translate(x, -y);
|
||||||
|
|
||||||
|
// 偏移
|
||||||
|
textLayer.translate(UnitValue("-1cm"), UnitValue("+0.5cm"));
|
||||||
|
|
||||||
|
// 复制并栅格化图层
|
||||||
|
var copyLayer = textLayer.duplicate();
|
||||||
|
copyLayer.rasterize(RasterizeType.ENTIRELAYER);
|
||||||
|
|
||||||
|
// 设置固定颜色值
|
||||||
|
var 固定颜色 = new SolidColor();
|
||||||
|
固定颜色.rgb.red = 255; // 红色分量
|
||||||
|
固定颜色.rgb.green = 255; // 绿色分量
|
||||||
|
固定颜色.rgb.blue = 255; // 蓝色分量
|
||||||
|
|
||||||
|
activeDocument.activeLayer = copyLayer;
|
||||||
|
activeDocument.selection.fill(固定颜色, ColorBlendMode.NORMAL, 100, true);
|
||||||
|
|
||||||
|
// 白边
|
||||||
|
activeDocument.activeLayer.applyMinimum(10);
|
||||||
|
后移一层()
|
||||||
|
向上选择()
|
||||||
|
向下合并()
|
||||||
|
名称更改字体()
|
||||||
|
缩小字体图层至文档一半()
|
||||||
|
多选背景()
|
||||||
|
底对齐()
|
||||||
|
水平居中对齐()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 后移一层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 向上选择() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(19);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
function 向下合并() //向下合并
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 名称更改字体() //名称更改
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putString(stringIDToTypeID("name"), "字体");
|
||||||
|
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 缩小字体图层至文档一半() {
|
||||||
|
var 文档 = app.activeDocument;
|
||||||
|
var 字体图层 = null;
|
||||||
|
|
||||||
|
// 遍历文档中的图层以找到名为"字体"的图层
|
||||||
|
for (var i = 0; i < 文档.artLayers.length; i++) {
|
||||||
|
if (文档.artLayers[i].name === "字体") {
|
||||||
|
字体图层 = 文档.artLayers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (字体图层 !== null) {
|
||||||
|
// 获取文档的宽度的一半
|
||||||
|
var 目标宽度 = 文档.width / 2;
|
||||||
|
|
||||||
|
// 获取图层的当前宽度
|
||||||
|
var 图层宽度 = 字体图层.bounds[2] - 字体图层.bounds[0];
|
||||||
|
|
||||||
|
// 计算缩放比例
|
||||||
|
var 缩放比例 = 目标宽度 / 图层宽度 * 100;
|
||||||
|
|
||||||
|
// 缩放图层
|
||||||
|
字体图层.resize(缩放比例, 缩放比例, AnchorPosition.MIDDLECENTER);
|
||||||
|
} else {
|
||||||
|
alert("未找到名为'字体'的图层");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 多选背景() //自由变换
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "背景");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(1);
|
||||||
|
list.putInteger(13);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 底对齐() //底对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSBottoms"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 水平居中对齐() //水平居中对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSCentersH"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
124
PSMARK代码块/取消监听.jsx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
function 取消事件监听() {
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
arg_num = arguments.length;
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
arguments = []; //初始赋值为空
|
||||||
|
}
|
||||||
|
|
||||||
|
eventName = "set";
|
||||||
|
|
||||||
|
//开启监听
|
||||||
|
//enable_notifier(eventName, $.fileName);
|
||||||
|
|
||||||
|
//取消监听
|
||||||
|
disable_notifier(eventName, $.fileName);
|
||||||
|
|
||||||
|
//这里进行监控调用事件
|
||||||
|
if (arguments.length >= 2)
|
||||||
|
{
|
||||||
|
//alert(arguments.length);
|
||||||
|
//~ alert(arguments[0],"动作参数1"); //动作描述符 AR
|
||||||
|
//~ alert(arguments[1],"动作参数2"); //动作事件ID
|
||||||
|
|
||||||
|
//alert("事件名:"+typeIDToStringID(arguments[1])+"\n"+"事件ID:"+arguments[1],"提示:");
|
||||||
|
moveToGuideCenter();
|
||||||
|
//main(arguments[0], arguments[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveToGuideCenter() {
|
||||||
|
if (app.documents.length === 0 || !app.activeDocument.selection.bounds) {
|
||||||
|
alert("没有打开的文档或没有有效的选区。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
var guides = doc.guides;
|
||||||
|
var xGuide = null;
|
||||||
|
var yGuide = null;
|
||||||
|
|
||||||
|
// 找到 X 方向和 Y 方向上的参考线
|
||||||
|
for (var i = 0; i < guides.length; i++) {
|
||||||
|
var guide = guides[i];
|
||||||
|
if (guide.direction === Direction.HORIZONTAL && yGuide === null) {
|
||||||
|
yGuide = guide;
|
||||||
|
} else if (guide.direction === Direction.VERTICAL && xGuide === null) {
|
||||||
|
xGuide = guide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xGuide === null || yGuide === null) {
|
||||||
|
alert("必须确保存在一条水平和一条垂直参考线。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var intersectionX = xGuide.coordinate;
|
||||||
|
var intersectionY = yGuide.coordinate;
|
||||||
|
|
||||||
|
var bounds = doc.selection.bounds;
|
||||||
|
var xCenter = bounds[0] + (bounds[2] - bounds[0]) / 2;
|
||||||
|
var yCenter = bounds[1] + (bounds[3] - bounds[1]) / 2;
|
||||||
|
|
||||||
|
var deltaX = intersectionX - xCenter;
|
||||||
|
var deltaY = intersectionY - yCenter;
|
||||||
|
|
||||||
|
// 取消当前选区
|
||||||
|
doc.selection.deselect();
|
||||||
|
|
||||||
|
// 移动选区图层
|
||||||
|
var selectedLayer = doc.activeLayer;
|
||||||
|
selectedLayer.translate(deltaX, deltaY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function enable_notifier(event_name, script_name, event_class)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (var i = 0; i < app.notifiers.length; i++)
|
||||||
|
{
|
||||||
|
if (app.notifiers[i].event == event_name &&
|
||||||
|
File(app.notifiers[i].eventFile).fsName.toLowerCase() == File(script_name).fsName.toLowerCase())
|
||||||
|
{
|
||||||
|
if (!app.notifiersEnabled) app.notifiersEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.notifiers.add(event_name, File(script_name), event_class);
|
||||||
|
app.notifiersEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (e) { _alert(e); return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function disable_notifier(event_name, script_name, event_class)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ret = false;
|
||||||
|
|
||||||
|
for (var i = 0; i < app.notifiers.length; i++)
|
||||||
|
{
|
||||||
|
if (app.notifiers[i].event == event_name &&
|
||||||
|
File(app.notifiers[i].eventFile).fsName.toLowerCase() == File(script_name).fsName.toLowerCase())
|
||||||
|
{
|
||||||
|
app.notifiers[i].remove();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!app.notifiers.length) app.notifiersEnabled = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (e) { _alert(e); return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
15
PSMARK代码块/图层选择.jsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
function 图层选择(sizelayername)
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), sizelayername);
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(10);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
62
PSMARK代码块/图案填充.jsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
var desktop = Folder.desktop;
|
||||||
|
var file = new File(desktop + "/名称数据.json");
|
||||||
|
if (file.open('r')) {
|
||||||
|
var content = file.read();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// 移除双引号
|
||||||
|
var content1 = content.replace(/"/g, '');
|
||||||
|
|
||||||
|
// 移除[]并使用逗号分割字符串
|
||||||
|
var 名称数组 = content1.replace(/^\s*\[|\]\s*$/g, '').split(',');
|
||||||
|
|
||||||
|
for (var k = 0; k < 名称数组.length; k++) {
|
||||||
|
var 名称 = 名称数组[k].replace(/^\s+|\s+$/g, ''); // 使用正则替代trim函数移除前后空格
|
||||||
|
alert(名称);
|
||||||
|
var matches = 名称.match(/\(([^)]+)\)/);
|
||||||
|
if (matches) {
|
||||||
|
var 图案名称 = matches[1];
|
||||||
|
var 素材填充 = app.activeDocument.layerSets.getByName("填充底图").layers.getByName(图案名称);
|
||||||
|
app.activeDocument.activeLayer = 素材填充;
|
||||||
|
载入选区();
|
||||||
|
填充图案(名称);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("找不到名称数据文件查看是否写入!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 载入选区() //载入选区
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 填充图案(名称) //新建图案填充图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("contentLayer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
var d2 = new ActionDescriptor();
|
||||||
|
var d3 = new ActionDescriptor();
|
||||||
|
d3.putString(stringIDToTypeID("name"), 名称);
|
||||||
|
|
||||||
|
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||||
|
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
24
PSMARK代码块/存储psd.jsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
function saveAsPSD(saveFolder) {
|
||||||
|
var doc = app.activeDocument; // 获取当前文档
|
||||||
|
|
||||||
|
if (doc == null) {
|
||||||
|
alert("没有打开的文档!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = doc.name.replace(/\.[^\.]+$/, ''); // 移除文件扩展名
|
||||||
|
var saveFile = File(saveFolder + "/" + fileName + ".psd"); // 创建保存路径
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putBoolean(stringIDToTypeID("maximizeCompatibility"), true);
|
||||||
|
d.putObject(stringIDToTypeID("as"), stringIDToTypeID("photoshop35Format"), d1);
|
||||||
|
d.putPath(stringIDToTypeID("in"), new File(saveFile));
|
||||||
|
d.putInteger(stringIDToTypeID("documentID"), 2027);
|
||||||
|
d.putBoolean(stringIDToTypeID("lowerCase"), true);
|
||||||
|
d.putEnumerated(stringIDToTypeID("saveStage"), stringIDToTypeID("saveStageType"), stringIDToTypeID("saveSucceeded"));
|
||||||
|
executeAction(stringIDToTypeID("save"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
996
PSMARK代码块/定位码快速换图.jsx
Normal file
@@ -0,0 +1,996 @@
|
|||||||
|
function 定位码批量化替换外链新(){
|
||||||
|
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
dialog.text = "定位码批量快速换图 ";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.preferredSize.width = 183;
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "文件夹选择";
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "大货齐码裁片模板路径:";
|
||||||
|
|
||||||
|
var button1 = panel1.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "路径选择";
|
||||||
|
button1.preferredSize.width = 300;
|
||||||
|
|
||||||
|
var statictext2 = panel1.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "待套花样路径选择:";
|
||||||
|
|
||||||
|
var button2 = panel1.add("button", undefined, undefined, {name: "button2"});
|
||||||
|
button2.text = "路径选择";
|
||||||
|
button2.preferredSize.width = 300;
|
||||||
|
|
||||||
|
|
||||||
|
var statictext3 = panel1.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "缓存切片裁片路径选择:";
|
||||||
|
|
||||||
|
var button3 = panel1.add("button", undefined, undefined, {name: "button3"});
|
||||||
|
button3.text = "路径选择";
|
||||||
|
button3.preferredSize.width = 300;
|
||||||
|
|
||||||
|
var statictext4= panel1.add("statictext", undefined, undefined, {name: "statictext4"});
|
||||||
|
statictext4.text = "大货成品路径选择:";
|
||||||
|
|
||||||
|
var button4 = panel1.add("button", undefined, undefined, {name: "button4"});
|
||||||
|
button4.text = "路径选择";
|
||||||
|
button4.preferredSize.width = 300;
|
||||||
|
|
||||||
|
// PANEL2
|
||||||
|
// ======
|
||||||
|
var panel2 = panel1.add("panel", undefined, undefined, {name: "panel2"});
|
||||||
|
panel2.text = "款号修改:";
|
||||||
|
panel2.orientation = "column";
|
||||||
|
panel2.alignChildren = ["left","top"];
|
||||||
|
panel2.spacing = 10;
|
||||||
|
panel2.margins = 10;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// PANEL4
|
||||||
|
// ======
|
||||||
|
|
||||||
|
|
||||||
|
var statictext11 = panel2.add("statictext", undefined, undefined, {name: "statictext11"});
|
||||||
|
statictext11.text = "是否拼合裁片组:";
|
||||||
|
|
||||||
|
var checkbox1 =panel2.add("checkbox", undefined, undefined, {name: "checkbox1"});
|
||||||
|
checkbox1.value = true;
|
||||||
|
checkbox1.text = "拼合";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var statictext13 = panel2.add("statictext", undefined, undefined, {name: "statictext13"});
|
||||||
|
statictext13.text = "存储位置:";
|
||||||
|
|
||||||
|
var checkbox3 =panel2.add("checkbox", undefined, undefined, {name: "checkbox3"});
|
||||||
|
checkbox3.value = false;
|
||||||
|
checkbox3.text = "模板位置";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = dialog.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "column";
|
||||||
|
group2.alignChildren = ["fill","top"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var ok = group2.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "执行";
|
||||||
|
|
||||||
|
var cancel = group2.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
|
||||||
|
var button8 = group2.add("button", undefined, undefined, {name: "button8"});
|
||||||
|
button8.text = "关于我们";
|
||||||
|
|
||||||
|
button1.onClick=function(){
|
||||||
|
|
||||||
|
button1.text =Folder.selectDialog ("大货齐码裁片模板路径:").fsName;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button2.onClick=function(){
|
||||||
|
|
||||||
|
button2.text =Folder.selectDialog ("待套花样路径选择:").fsName;
|
||||||
|
|
||||||
|
}
|
||||||
|
button3.onClick=function(){
|
||||||
|
|
||||||
|
button3.text =Folder.selectDialog ("缓存切片裁片路径选择:").fsName;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
button4.onClick=function(){
|
||||||
|
|
||||||
|
button4.text =Folder.selectDialog ("大货成品路径选择:").fsName;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
button8.onClick = function () {
|
||||||
|
|
||||||
|
alert("自由花型工作室 17520145271 脚本开发 裁片排版 花型开发 ",dialog.text+"----关于我们");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ok .onClick=function(){
|
||||||
|
|
||||||
|
|
||||||
|
var 大货齐码裁片模板路径 = new Folder(button1.text);
|
||||||
|
var 待套花样路径选择 =new Folder(button2.text);
|
||||||
|
var 大货文件存放位置 =new Folder(button4.text);
|
||||||
|
var 缓存切片裁片路径选择 = new Folder(button3.text);
|
||||||
|
|
||||||
|
var myFiles1 =大货齐码裁片模板路径.getFiles("*.tif*");
|
||||||
|
var myFiles2 =待套花样路径选择.getFiles("*.tif*");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (var i = 0; i <myFiles2.length; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
app.open(myFiles2[i])
|
||||||
|
文档名称1=activeDocument.name.replace(/(?:\.[^.]*$|$)/, '');
|
||||||
|
var 大货成品文件夹 =文档名称1
|
||||||
|
//花样标准化(80);
|
||||||
|
// 文档另存(缓存切片裁片路径选择);
|
||||||
|
花样图层导出为TIF透明底(缓存切片裁片路径选择)
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES)
|
||||||
|
|
||||||
|
|
||||||
|
var parentFolderPath =大货文件存放位置
|
||||||
|
var folderNames = 文档名称1
|
||||||
|
|
||||||
|
var folderPath = parentFolderPath + "/" + 文档名称1;
|
||||||
|
var folderObj = new Folder(folderPath);
|
||||||
|
$.writeln(folderObj);
|
||||||
|
folderObj.create();
|
||||||
|
|
||||||
|
|
||||||
|
for (var j = 0; j <myFiles1.length; j++) {
|
||||||
|
app.open(myFiles1[j])
|
||||||
|
|
||||||
|
|
||||||
|
更换当前文档裁片组外链(缓存切片裁片路径选择);
|
||||||
|
// 图层选择();
|
||||||
|
// app.activeDocument.activeLayer.textItem.contents = 文档名称1;
|
||||||
|
选择裁片图层();
|
||||||
|
|
||||||
|
// if (checkbox1.value == true) {
|
||||||
|
合并图层();
|
||||||
|
预设图案填充(缓存切片裁片路径选择)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
另存为(folderObj,文档名称1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 预设图案填充(缓存切片裁片路径选择)
|
||||||
|
{
|
||||||
|
|
||||||
|
var file = new File(缓存切片裁片路径选择 + "/名称数据.json");
|
||||||
|
if (file.open('r')) {
|
||||||
|
var content = file.read();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// 移除双引号
|
||||||
|
var content1 = content.replace(/"/g, '');
|
||||||
|
|
||||||
|
// 移除[]并使用逗号分割字符串
|
||||||
|
var 名称数组 = content1.replace(/^\s*\[|\]\s*$/g, '').split(',');
|
||||||
|
|
||||||
|
for (var k = 0; k < 名称数组.length; k++) {
|
||||||
|
var 名称 = 名称数组[k].replace(/^\s+|\s+$/g, ''); // 使用正则替代trim函数移除前后空格
|
||||||
|
//alert(名称);
|
||||||
|
var matches = 名称.match(/\(([^)]+)\)/);
|
||||||
|
if (matches) {
|
||||||
|
var 图案名称 = matches[1];
|
||||||
|
var 素材填充 = app.activeDocument.layerSets.getByName("填充底图").layers.getByName(图案名称);
|
||||||
|
app.activeDocument.activeLayer = 素材填充;
|
||||||
|
载入选区();
|
||||||
|
填充图案(名称);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("找不到名称数据文件查看是否写入!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 载入选区() //载入选区
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 填充图案(名称) //新建图案填充图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("contentLayer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
var d2 = new ActionDescriptor();
|
||||||
|
var d3 = new ActionDescriptor();
|
||||||
|
d3.putString(stringIDToTypeID("name"), 名称);
|
||||||
|
|
||||||
|
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||||
|
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 载入选区() //载入选区
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 填充图案(图案名称) //新建图案填充图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("contentLayer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
var d2 = new ActionDescriptor();
|
||||||
|
var d3 = new ActionDescriptor();
|
||||||
|
d3.putString(stringIDToTypeID("name"), 图案名称);
|
||||||
|
d3.putString(stringIDToTypeID("ID"), 图案名称);
|
||||||
|
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||||
|
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 花样图层导出为TIF透明底(导出目录) {
|
||||||
|
|
||||||
|
app.preferences.rulerUnits = Units.MM;
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
var 扩展毫米数=80
|
||||||
|
|
||||||
|
// 获取文档中的所有图层
|
||||||
|
var allLayers = doc.layers;
|
||||||
|
var 名称数组 = [];
|
||||||
|
// 循环遍历所有图层
|
||||||
|
for (var i = 0; i < allLayers.length; i++) {
|
||||||
|
// 检查图层是否是图层组
|
||||||
|
if (allLayers[i] instanceof LayerSet) {
|
||||||
|
// 获取图层组中的所有子图层
|
||||||
|
subLayers = allLayers[i].layers;
|
||||||
|
图层组名称=allLayers[i].name
|
||||||
|
文档名称=app.activeDocument.name
|
||||||
|
文档名称去除后缀 = 文档名称.replace(/\.[^\.]+$/, "");
|
||||||
|
// 检查图层组是否包含子图层
|
||||||
|
if (subLayers.length > 0) {
|
||||||
|
// 获取图层组中最后一个子图层的名称
|
||||||
|
var lastSubLayer = subLayers[subLayers.length - 1];
|
||||||
|
var lastSubLayerName = lastSubLayer.name;
|
||||||
|
FastSubLayer = subLayers[0];
|
||||||
|
FastSubLayername= FastSubLayer.name
|
||||||
|
// 输出图层组名称和最后一个子图层的名称
|
||||||
|
|
||||||
|
|
||||||
|
if (图层组名称 === "填充底图") {
|
||||||
|
for (var y = 0; y < subLayers.length; y++) {
|
||||||
|
// alert(subLayers[j].name);
|
||||||
|
填充底图组内部循环名称 = subLayers[y].name
|
||||||
|
填充底图循环素材 = app.activeDocument.layerSets.getByName("填充底图").layers.getByName(填充底图组内部循环名称);
|
||||||
|
app.activeDocument.activeLayer = 填充底图循环素材;
|
||||||
|
新建文档()
|
||||||
|
// 合并图层()
|
||||||
|
载入选区()
|
||||||
|
裁剪()
|
||||||
|
名称=文档名称去除后缀+"-("+填充底图组内部循环名称 +")-填充底图"
|
||||||
|
名称数组.push('"' + 名称 + '"');
|
||||||
|
制作图案预设(名称)
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
app.activeDocument=doc
|
||||||
|
// 执行某一个操作(例如,设置图层组的可见性)
|
||||||
|
// alert("测试")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 手动创建JSON格式的字符串
|
||||||
|
var jsonStr = '[' + 名称数组.join(', ') + ']';
|
||||||
|
|
||||||
|
// 创建一个文件对象指向桌面
|
||||||
|
|
||||||
|
var file = new File(导出目录 + "/名称数据.json");
|
||||||
|
|
||||||
|
// 打开文件,写入JSON字符串,然后关闭文件
|
||||||
|
file.open('w');
|
||||||
|
file.write(jsonStr);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 执行别的操作(例如,隐藏其他图层组)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
空白裁片模板 = app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(lastSubLayerName);
|
||||||
|
app.activeDocument.activeLayer = 空白裁片模板;
|
||||||
|
新建图层()
|
||||||
|
app.activeDocument.activeLayer.name="最大白边值"
|
||||||
|
裁片边界 = lastSubLayer.bounds;
|
||||||
|
扩展值 = 毫米转像素(扩展毫米数); //50cm
|
||||||
|
裁片边界_左 = 毫米转像素(裁片边界[0]) - 扩展值;
|
||||||
|
|
||||||
|
裁片边界_上 = 毫米转像素(裁片边界[1]) - 扩展值;
|
||||||
|
裁片边界_右 = 毫米转像素(裁片边界[2]) + 扩展值;
|
||||||
|
裁片边界_下 = 毫米转像素(裁片边界[3]) + 扩展值;
|
||||||
|
|
||||||
|
var selRegion = [
|
||||||
|
[裁片边界_左,裁片边界_上],
|
||||||
|
[裁片边界_右,裁片边界_上],
|
||||||
|
[裁片边界_右,裁片边界_下],
|
||||||
|
[裁片边界_左,裁片边界_下]
|
||||||
|
];
|
||||||
|
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||||
|
背景切换();
|
||||||
|
恢复白底();
|
||||||
|
填充();
|
||||||
|
隐藏图层();
|
||||||
|
裁片图层组 = app.activeDocument.layerSets.getByName(图层组名称)
|
||||||
|
app.activeDocument.activeLayer = 裁片图层组 ;
|
||||||
|
新建文档()
|
||||||
|
空白裁片模板 = app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(lastSubLayerName);
|
||||||
|
app.activeDocument.activeLayer = 空白裁片模板;
|
||||||
|
删除图层()
|
||||||
|
最大白边值=app.activeDocument.layerSets.getByName(图层组名称).layers.getByName("最大白边值");
|
||||||
|
app.activeDocument.activeLayer = 最大白边值;
|
||||||
|
app.activeDocument.crop(最大白边值.bounds, 0);
|
||||||
|
// 保存为TIF
|
||||||
|
var 文件路径 = 导出目录 + "/" + 图层组名称 + ".tif";
|
||||||
|
tiffOptions = new TiffSaveOptions();
|
||||||
|
app.activeDocument.saveAs(new File(文件路径), tiffOptions);
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
app.activeDocument=doc
|
||||||
|
最大白边值=app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(FastSubLayername);
|
||||||
|
app.activeDocument.activeLayer = 最大白边值;
|
||||||
|
//上移图层()
|
||||||
|
多选图层()
|
||||||
|
转换为智能对象()
|
||||||
|
添加图层蒙版()
|
||||||
|
// 向下合并()
|
||||||
|
转换为智能对象()
|
||||||
|
// 重新链接到文件(文件路径)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function 恢复白底() //删除图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("exchange"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 制作图案预设(名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("pattern"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||||
|
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putString(stringIDToTypeID("name"), 名称);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 合并图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 背景切换() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("reset"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 裁剪() //裁剪
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putBoolean(stringIDToTypeID("delete"), true);
|
||||||
|
executeAction(stringIDToTypeID("crop"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 重新链接到文件(文件路径) //重新链接到文件
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putPath(stringIDToTypeID("null"), new File(文件路径));
|
||||||
|
d.putInteger(stringIDToTypeID("layerID"), 94);
|
||||||
|
executeAction(stringIDToTypeID("placedLayerRelinkToFile"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 转换为智能对象() //转换为智能对象
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("newPlacedLayer"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 载入选区() //载入选区
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 向下合并() //向下合并
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 添加图层蒙版() //添加图层蒙版
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||||
|
d.putReference(stringIDToTypeID("at"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 上移图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(11);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 毫米转像素(毫米)
|
||||||
|
{
|
||||||
|
//厘米转像素
|
||||||
|
doc_w = app.activeDocument.width;
|
||||||
|
//用户设定的厘米数 支持小数
|
||||||
|
user_mm = UnitValue(毫米,"mm");
|
||||||
|
user_px = user_mm.as("px")*app.activeDocument.resolution/72;
|
||||||
|
return user_px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 删除图层() //删除图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(22);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 多选图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "最大白边值");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelectionContinuous"));
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
|
||||||
|
list.putInteger(115);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 新建图层() //新建图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("layer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putBoolean(stringIDToTypeID("group"), true);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("layer"), d1);
|
||||||
|
d.putInteger(stringIDToTypeID("layerID"), 22);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 填充() //填充
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("fillContents"), stringIDToTypeID("foregroundColor"));
|
||||||
|
d.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||||
|
d.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||||
|
executeAction(stringIDToTypeID("fill"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 隐藏图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var list = new ActionList();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
list.putReference(r);
|
||||||
|
d.putList(stringIDToTypeID("null"), list);
|
||||||
|
executeAction(stringIDToTypeID("hide"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 新建文档() //复制图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("document"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putInteger(stringIDToTypeID("version"), 5);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 更换当前文档裁片组外链(缓存切片裁片路径选择)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
裁片组 = app.activeDocument.layerSets.getByName("裁片").layers;
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
alert("找不到裁片组");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var k=0;k<裁片组.length;k++)
|
||||||
|
{
|
||||||
|
裁片 = 裁片组[k];
|
||||||
|
app.activeDocument.activeLayer = 裁片;
|
||||||
|
if(裁片.kind == LayerKind.SMARTOBJECT)
|
||||||
|
{
|
||||||
|
更换链接智能对象路径(缓存切片裁片路径选择);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 更换链接智能对象路径(缓存切片裁片路径选择)
|
||||||
|
{
|
||||||
|
//获取当前图层外链的智能对象路径
|
||||||
|
//先获取链接的文件名
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
//~ r.putName(charIDToTypeID("Lyr "), "◆左袖口"); //按名称查找
|
||||||
|
descLayer = executeActionGet(r);
|
||||||
|
res = descLayer.getObjectValue(stringIDToTypeID("smartObject"));
|
||||||
|
|
||||||
|
链接文件名 = res.getString(stringIDToTypeID("fileReference"));
|
||||||
|
//$.writeln(链接文件名);
|
||||||
|
|
||||||
|
//~ 链接文件路径 = res.getPath(stringIDToTypeID("link"));
|
||||||
|
//~ $.writeln(链接文件路径);
|
||||||
|
|
||||||
|
图片路径 = 缓存切片裁片路径选择 + "/" + 链接文件名;
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putPath(stringIDToTypeID("null"), new File(图片路径));
|
||||||
|
executeAction(stringIDToTypeID("placedLayerRelinkToFile"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 另存为(folderObj,文档名称1)
|
||||||
|
{
|
||||||
|
文档名称=activeDocument.name.replace(/(?:\.[^.]*$|$)/, '');
|
||||||
|
saveIn=File(folderObj+ "/"+文档名称1+"-"+文档名称);
|
||||||
|
tifSaveOpt = new TiffSaveOptions();
|
||||||
|
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||||
|
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||||
|
asCopy=true
|
||||||
|
app.activeDocument.saveAs(saveIn,tifSaveOpt,asCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 图层选择() //a
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "款号");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(74);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
//alert("找不到款号图层",dialog.text+"----关于");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
function 合并图层() //合并图层
|
||||||
|
{
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 选择裁片图层() //
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "裁片");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(74);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
alert("找不到裁片图层",dialog.text+"----关于");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 存储在原来位置(myFolder)
|
||||||
|
{
|
||||||
|
tiffOptions = new TiffSaveOptions();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
app.activeDocument.saveAs(new File(文件路径), tiffOptions);
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
alert(文件太大无法保存)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 花样标准化(扩展毫米数)
|
||||||
|
{
|
||||||
|
var 扩展毫米数=80
|
||||||
|
app.preferences.rulerUnits = Units.MM;
|
||||||
|
|
||||||
|
裁片组 = app.activeDocument.layerSets;
|
||||||
|
|
||||||
|
for(var z=0;z<裁片组.length;z++)
|
||||||
|
{
|
||||||
|
当前裁片组 = 裁片组[z];
|
||||||
|
花样图层 = 当前裁片组.layers[0];
|
||||||
|
裁片图层 = 当前裁片组.layers[1];
|
||||||
|
|
||||||
|
裁片边界 = 裁片图层.bounds;
|
||||||
|
//~ alert(毫米转像素(50))
|
||||||
|
//~ 扩展值 = 毫米转像素(50); //50cm
|
||||||
|
扩展值 = 毫米转像素(扩展毫米数); //50cm
|
||||||
|
裁片边界_左 = 毫米转像素(裁片边界[0]) - 扩展值;
|
||||||
|
裁片边界_上 = 毫米转像素(裁片边界[1]) - 扩展值;
|
||||||
|
裁片边界_右 = 毫米转像素(裁片边界[2]) + 扩展值;
|
||||||
|
裁片边界_下 = 毫米转像素(裁片边界[3]) + 扩展值;
|
||||||
|
|
||||||
|
|
||||||
|
//左上右下点XY坐标
|
||||||
|
var selRegion = [
|
||||||
|
[裁片边界_左,裁片边界_上],
|
||||||
|
[裁片边界_右,裁片边界_上],
|
||||||
|
[裁片边界_右,裁片边界_下],
|
||||||
|
[裁片边界_左,裁片边界_下]
|
||||||
|
];
|
||||||
|
|
||||||
|
app.activeDocument.activeLayer = 花样图层;
|
||||||
|
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||||
|
按选区添加蒙版();
|
||||||
|
|
||||||
|
//制作一个白底衬底图
|
||||||
|
//新建一个图层
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("layer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putInteger(stringIDToTypeID("layerID"), 198);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
白底图层 = app.activeDocument.activeLayer;
|
||||||
|
白底图层.name = "白底";
|
||||||
|
|
||||||
|
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||||
|
|
||||||
|
var c = new SolidColor();
|
||||||
|
c.rgb.hexValue = "FFFFFF";
|
||||||
|
app.activeDocument.selection.fill(c);
|
||||||
|
|
||||||
|
花样图层.grouped = false;
|
||||||
|
白底图层.move(花样图层,ElementPlacement.PLACEAFTER);
|
||||||
|
|
||||||
|
|
||||||
|
app.activeDocument.activeLayer = 花样图层;
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "白底");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelectionContinuous"));
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
app.activeDocument.activeLayer.merge(); //合并当前选择图层
|
||||||
|
app.activeDocument.activeLayer.grouped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 毫米转像素(毫米)
|
||||||
|
{
|
||||||
|
//厘米转像素
|
||||||
|
doc_w = app.activeDocument.width;
|
||||||
|
//用户设定的厘米数 支持小数
|
||||||
|
user_mm = UnitValue(毫米,"mm");
|
||||||
|
user_px = user_mm.as("px")*app.activeDocument.resolution/72;
|
||||||
|
return user_px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 按选区添加蒙版() //先创建出选区 然后按选区添加出一个蒙版
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||||
|
d.putReference(stringIDToTypeID("at"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 文档另存(缓存切片裁片路径选择){
|
||||||
|
|
||||||
|
var 导出目录 =缓存切片裁片路径选择;
|
||||||
|
var 裁片组 = app.activeDocument.layerSets;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for(var i=0;i<裁片组.length;i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
app.activeDocument.duplicate("temp");
|
||||||
|
|
||||||
|
复制文档裁片组 = app.activeDocument.layerSets;
|
||||||
|
当前裁片组 = 复制文档裁片组[i];
|
||||||
|
当前裁片组名 = 当前裁片组.name;
|
||||||
|
花样图层 = 当前裁片组.layers[0];
|
||||||
|
裁片图层 = 当前裁片组.layers[1];
|
||||||
|
|
||||||
|
app.activeDocument.activeLayer = 花样图层;
|
||||||
|
//把花样图层导出
|
||||||
|
花样图层.grouped = false; //取消图层链接
|
||||||
|
|
||||||
|
仅当前图层可见();
|
||||||
|
|
||||||
|
//按花样图层大小裁剪文档
|
||||||
|
app.activeDocument.crop(花样图层.bounds,0);
|
||||||
|
|
||||||
|
//拼合图像只保留花样图层
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("flattenImage"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
//保存为TIF
|
||||||
|
var 文件路径 = 导出目录 + "/" + 当前裁片组名 + ".tif";
|
||||||
|
tiffOptions = new TiffSaveOptions();
|
||||||
|
app.activeDocument.saveAs(new File(文件路径), tiffOptions);
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 仅当前图层可见()
|
||||||
|
{
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var list = new ActionList();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
list.putReference(r);
|
||||||
|
d.putList(stringIDToTypeID("null"), list);
|
||||||
|
d.putBoolean(stringIDToTypeID("toggleOptionsPalette"), true);
|
||||||
|
executeAction(stringIDToTypeID("show"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
569
PSMARK代码块/定位码快速超链接.jsx
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 快速定位码链接() {
|
||||||
|
|
||||||
|
app.activeDocument.suspendHistory("快速定位码链接", "花样图层导出()");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 花样图层导出() {
|
||||||
|
var 导出目录 = Folder.selectDialog("选择外链素材目录");
|
||||||
|
if (!导出目录) {
|
||||||
|
alert("未选择导出目录。操作已取消。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
花样图层导出为TIF透明底(导出目录)
|
||||||
|
// 花样图层导出为TIF(导出目录);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 花样图层导出为TIF透明底(导出目录) {
|
||||||
|
|
||||||
|
app.preferences.rulerUnits = Units.MM;
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
var 扩展毫米数=80
|
||||||
|
|
||||||
|
// 获取文档中的所有图层
|
||||||
|
var allLayers = doc.layers;
|
||||||
|
|
||||||
|
var 名称数组 = [];
|
||||||
|
// 循环遍历所有图层
|
||||||
|
for (var i = 0; i < allLayers.length; i++) {
|
||||||
|
// 检查图层是否是图层组
|
||||||
|
if (allLayers[i] instanceof LayerSet) {
|
||||||
|
// 获取图层组中的所有子图层
|
||||||
|
subLayers = allLayers[i].layers;
|
||||||
|
图层组名称=allLayers[i].name
|
||||||
|
文档名称=app.activeDocument.name
|
||||||
|
文档名称去除后缀 = 文档名称.replace(/\.[^\.]+$/, "");
|
||||||
|
// 检查图层组是否包含子图层
|
||||||
|
if (subLayers.length > 0) {
|
||||||
|
// 获取图层组中最后一个子图层的名称
|
||||||
|
var lastSubLayer = subLayers[subLayers.length - 1];
|
||||||
|
var lastSubLayerName = lastSubLayer.name;
|
||||||
|
var lastSubLayerName = lastSubLayer.name;
|
||||||
|
FastSubLayer = subLayers[0];
|
||||||
|
FastSubLayername= FastSubLayer.name
|
||||||
|
|
||||||
|
//alert(SastSubLayerName)
|
||||||
|
// 输出图层组名称和最后一个子图层的名称
|
||||||
|
|
||||||
|
|
||||||
|
if (图层组名称 === "填充底图") {
|
||||||
|
for (var j = 0; j < subLayers.length; j++) {
|
||||||
|
// alert(subLayers[j].name);
|
||||||
|
填充底图组内部循环名称 = subLayers[j].name
|
||||||
|
填充底图循环素材 = app.activeDocument.layerSets.getByName("填充底图").layers.getByName(填充底图组内部循环名称);
|
||||||
|
app.activeDocument.activeLayer = 填充底图循环素材;
|
||||||
|
新建文档()
|
||||||
|
// 合并图层()
|
||||||
|
载入选区()
|
||||||
|
裁剪()
|
||||||
|
名称=文档名称去除后缀+"-("+填充底图组内部循环名称 +")-填充底图"
|
||||||
|
名称数组.push('"' + 名称 + '"');
|
||||||
|
制作图案预设(名称)
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
app.activeDocument=doc
|
||||||
|
// 执行某一个操作(例如,设置图层组的可见性)
|
||||||
|
// alert("测试")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 手动创建JSON格式的字符串
|
||||||
|
var jsonStr = '[' + 名称数组.join(', ') + ']';
|
||||||
|
|
||||||
|
// 创建一个文件对象指向桌面
|
||||||
|
|
||||||
|
var file = new File(导出目录 + "/名称数据.json");
|
||||||
|
|
||||||
|
// 打开文件,写入JSON字符串,然后关闭文件
|
||||||
|
file.open('w');
|
||||||
|
file.write(jsonStr);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 执行别的操作(例如,隐藏其他图层组)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
空白裁片模板 = app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(lastSubLayerName);
|
||||||
|
app.activeDocument.activeLayer = 空白裁片模板;
|
||||||
|
新建图层()
|
||||||
|
app.activeDocument.activeLayer.name="最大白边值"
|
||||||
|
裁片边界 = lastSubLayer.bounds;
|
||||||
|
扩展值 = 毫米转像素(扩展毫米数); //50cm
|
||||||
|
裁片边界_左 = 毫米转像素(裁片边界[0]) - 扩展值;
|
||||||
|
|
||||||
|
裁片边界_上 = 毫米转像素(裁片边界[1]) - 扩展值;
|
||||||
|
裁片边界_右 = 毫米转像素(裁片边界[2]) + 扩展值;
|
||||||
|
裁片边界_下 = 毫米转像素(裁片边界[3]) + 扩展值;
|
||||||
|
|
||||||
|
var selRegion = [
|
||||||
|
[裁片边界_左,裁片边界_上],
|
||||||
|
[裁片边界_右,裁片边界_上],
|
||||||
|
[裁片边界_右,裁片边界_下],
|
||||||
|
[裁片边界_左,裁片边界_下]
|
||||||
|
];
|
||||||
|
app.activeDocument.selection.select(selRegion, SelectionType.REPLACE);
|
||||||
|
背景切换();
|
||||||
|
恢复白底();
|
||||||
|
填充();
|
||||||
|
隐藏图层();
|
||||||
|
裁片图层组 = app.activeDocument.layerSets.getByName(图层组名称)
|
||||||
|
app.activeDocument.activeLayer = 裁片图层组 ;
|
||||||
|
新建文档()
|
||||||
|
空白裁片模板 = app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(lastSubLayerName);
|
||||||
|
app.activeDocument.activeLayer = 空白裁片模板;
|
||||||
|
删除图层()
|
||||||
|
最大白边值=app.activeDocument.layerSets.getByName(图层组名称).layers.getByName("最大白边值");
|
||||||
|
app.activeDocument.activeLayer = 最大白边值;
|
||||||
|
app.activeDocument.crop(最大白边值.bounds, 0);
|
||||||
|
|
||||||
|
// 保存为TIF
|
||||||
|
var 文件路径 = 导出目录 + "/" + 图层组名称 + ".tif";
|
||||||
|
tiffOptions = new TiffSaveOptions();
|
||||||
|
app.activeDocument.saveAs(new File(文件路径), tiffOptions);
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
app.activeDocument=doc
|
||||||
|
最大白边值=app.activeDocument.layerSets.getByName(图层组名称).layers.getByName(FastSubLayername);
|
||||||
|
app.activeDocument.activeLayer = 最大白边值;
|
||||||
|
//上移图层()
|
||||||
|
多选图层()
|
||||||
|
转换为智能对象()
|
||||||
|
释放剪贴蒙版()
|
||||||
|
栅格化图层()
|
||||||
|
选择下一图层()
|
||||||
|
新建图层()
|
||||||
|
背景切换();
|
||||||
|
恢复白底();
|
||||||
|
填充();
|
||||||
|
选择上一图层()
|
||||||
|
添加图层蒙版()
|
||||||
|
向下合并()
|
||||||
|
转换为智能对象()
|
||||||
|
重新链接到文件(文件路径)
|
||||||
|
创建剪贴蒙版()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function 恢复白底() //删除图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("exchange"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 创建剪贴蒙版() //创建剪贴蒙版
|
||||||
|
|
||||||
|
{
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("groupEvent"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 制作图案预设(名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("pattern"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||||
|
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putString(stringIDToTypeID("name"), 名称);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeLayersNew_72799682617188() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 合并图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 背景切换() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("reset"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 图层可见性show() //图层可见性
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var list = new ActionList();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
list.putReference(r);
|
||||||
|
d.putList(stringIDToTypeID("null"), list);
|
||||||
|
executeAction(stringIDToTypeID("show"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 裁剪() //裁剪
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putBoolean(stringIDToTypeID("delete"), true);
|
||||||
|
executeAction(stringIDToTypeID("crop"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 重新链接到文件(文件路径) //重新链接到文件
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putPath(stringIDToTypeID("null"), new File(文件路径));
|
||||||
|
d.putInteger(stringIDToTypeID("layerID"), 94);
|
||||||
|
executeAction(stringIDToTypeID("placedLayerRelinkToFile"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 转换为智能对象() //转换为智能对象
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("newPlacedLayer"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 载入选区() //载入选区
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 向下合并() //向下合并
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 添加图层蒙版() //添加图层蒙版
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||||
|
d.putReference(stringIDToTypeID("at"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 上移图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(11);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 毫米转像素(毫米)
|
||||||
|
{
|
||||||
|
//厘米转像素
|
||||||
|
doc_w = app.activeDocument.width;
|
||||||
|
//用户设定的厘米数 支持小数
|
||||||
|
user_mm = UnitValue(毫米,"mm");
|
||||||
|
user_px = user_mm.as("px")*app.activeDocument.resolution/72;
|
||||||
|
return user_px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 删除图层() //删除图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(22);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 多选图层(SastSubLayerName) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "最大白边值");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelectionContinuous"));
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
|
||||||
|
list.putInteger(115);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 新建图层() //新建图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("layer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putBoolean(stringIDToTypeID("group"), true);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("layer"), d1);
|
||||||
|
d.putInteger(stringIDToTypeID("layerID"), 22);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 释放剪贴蒙版() //释放剪贴蒙版
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("ungroup"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 添加图层蒙版() //添加图层蒙版
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||||
|
d.putReference(stringIDToTypeID("at"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 向下合并() //向下合并
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 栅格化图层() //栅格化图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("rasterizeLayer"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 选择下一图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("backwardEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(314);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 选择上一图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(369);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
function 新建图层() //新建图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("layer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putInteger(stringIDToTypeID("layerID"), 373);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 恢复白底() //删除图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("exchange"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 背景切换() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("color"), stringIDToTypeID("colors"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("reset"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 填充() //填充
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("fillContents"), stringIDToTypeID("foregroundColor"));
|
||||||
|
d.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||||
|
d.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||||
|
executeAction(stringIDToTypeID("fill"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 填充() //填充
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("fillContents"), stringIDToTypeID("foregroundColor"));
|
||||||
|
d.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||||
|
d.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||||
|
executeAction(stringIDToTypeID("fill"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 隐藏图层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var list = new ActionList();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
list.putReference(r);
|
||||||
|
d.putList(stringIDToTypeID("null"), list);
|
||||||
|
executeAction(stringIDToTypeID("hide"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 新建文档() //复制图层
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("document"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putInteger(stringIDToTypeID("version"), 5);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
30
PSMARK代码块/对齐居中方式.jsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
底对齐()
|
||||||
|
水平居中对齐()
|
||||||
|
function 底对齐() //底对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSBottoms"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 水平居中对齐() //水平居中对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSCentersH"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
124
PSMARK代码块/开启事件监听.jsx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
function 开启事件监听() {
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
arg_num = arguments.length;
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
arguments = []; //初始赋值为空
|
||||||
|
}
|
||||||
|
|
||||||
|
eventName = "set";
|
||||||
|
|
||||||
|
//开启监听
|
||||||
|
enable_notifier(eventName, $.fileName);
|
||||||
|
alert("开启成功")
|
||||||
|
//取消监听
|
||||||
|
//disable_notifier(eventName, $.fileName);
|
||||||
|
|
||||||
|
//这里进行监控调用事件
|
||||||
|
if (arguments.length >= 2)
|
||||||
|
{
|
||||||
|
//alert(arguments.length);
|
||||||
|
//~ alert(arguments[0],"动作参数1"); //动作描述符 AR
|
||||||
|
//~ alert(arguments[1],"动作参数2"); //动作事件ID
|
||||||
|
|
||||||
|
//alert("事件名:"+typeIDToStringID(arguments[1])+"\n"+"事件ID:"+arguments[1],"提示:");
|
||||||
|
moveToGuideCenter();
|
||||||
|
//main(arguments[0], arguments[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveToGuideCenter() {
|
||||||
|
if (app.documents.length === 0 || !app.activeDocument.selection.bounds) {
|
||||||
|
alert("没有打开的文档或没有有效的选区。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
var guides = doc.guides;
|
||||||
|
var xGuide = null;
|
||||||
|
var yGuide = null;
|
||||||
|
|
||||||
|
// 找到 X 方向和 Y 方向上的参考线
|
||||||
|
for (var i = 0; i < guides.length; i++) {
|
||||||
|
var guide = guides[i];
|
||||||
|
if (guide.direction === Direction.HORIZONTAL && yGuide === null) {
|
||||||
|
yGuide = guide;
|
||||||
|
} else if (guide.direction === Direction.VERTICAL && xGuide === null) {
|
||||||
|
xGuide = guide;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xGuide === null || yGuide === null) {
|
||||||
|
alert("必须确保存在一条水平和一条垂直参考线。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var intersectionX = xGuide.coordinate;
|
||||||
|
var intersectionY = yGuide.coordinate;
|
||||||
|
|
||||||
|
var bounds = doc.selection.bounds;
|
||||||
|
var xCenter = bounds[0] + (bounds[2] - bounds[0]) / 2;
|
||||||
|
var yCenter = bounds[1] + (bounds[3] - bounds[1]) / 2;
|
||||||
|
|
||||||
|
var deltaX = intersectionX - xCenter;
|
||||||
|
var deltaY = intersectionY - yCenter;
|
||||||
|
|
||||||
|
// 取消当前选区
|
||||||
|
doc.selection.deselect();
|
||||||
|
|
||||||
|
// 移动选区图层
|
||||||
|
var selectedLayer = doc.activeLayer;
|
||||||
|
selectedLayer.translate(deltaX, deltaY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function enable_notifier(event_name, script_name, event_class)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (var i = 0; i < app.notifiers.length; i++)
|
||||||
|
{
|
||||||
|
if (app.notifiers[i].event == event_name &&
|
||||||
|
File(app.notifiers[i].eventFile).fsName.toLowerCase() == File(script_name).fsName.toLowerCase())
|
||||||
|
{
|
||||||
|
if (!app.notifiersEnabled) app.notifiersEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.notifiers.add(event_name, File(script_name), event_class);
|
||||||
|
app.notifiersEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (e) { _alert(e); return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function disable_notifier(event_name, script_name, event_class)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ret = false;
|
||||||
|
|
||||||
|
for (var i = 0; i < app.notifiers.length; i++)
|
||||||
|
{
|
||||||
|
if (app.notifiers[i].event == event_name &&
|
||||||
|
File(app.notifiers[i].eventFile).fsName.toLowerCase() == File(script_name).fsName.toLowerCase())
|
||||||
|
{
|
||||||
|
app.notifiers[i].remove();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!app.notifiers.length) app.notifiersEnabled = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (e) { _alert(e); return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
51
PSMARK代码块/批量建立蒙蔽.jsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
function 删除图层3()
|
||||||
|
{
|
||||||
|
app.activeDocument.suspendHistory("Establish a mask", "批量建立蒙版()");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 批量建立蒙版(){
|
||||||
|
|
||||||
|
var doc = app.activeDocument; // 获取当前文档
|
||||||
|
// 遍历所有图层
|
||||||
|
for (var i = 0; i < doc.artLayers.length; i++) {
|
||||||
|
var layer = doc.artLayers[i];
|
||||||
|
// alert(layer.name); // 显示图层名称
|
||||||
|
var layname=layer.name
|
||||||
|
var 图层名称选择 = app.activeDocument.layers.getByName(layname);
|
||||||
|
app.activeDocument.activeLayer = 图层名称选择;
|
||||||
|
载入选区();
|
||||||
|
添加图层蒙版();
|
||||||
|
}
|
||||||
|
|
||||||
|
function 载入选区() //载入选区
|
||||||
|
{
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("transparencyEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 添加图层蒙版() //添加图层蒙版
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putClass(stringIDToTypeID("new"), stringIDToTypeID("channel"));
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||||
|
d.putReference(stringIDToTypeID("at"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("userMaskEnabled"), stringIDToTypeID("revealSelection"));
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
18
PSMARK代码块/批量换图.jsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// 获取Photoshop的当前文档
|
||||||
|
var 当前文档 = app.activeDocument;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取名为"贴图位置"的图层组
|
||||||
|
var layerSet = 当前文档.layerSets.getByName("贴图位置");
|
||||||
|
|
||||||
|
// 遍历图层组中的每个图层
|
||||||
|
for (var i = 0; i < layerSet.artLayers.length; i++) {
|
||||||
|
var layer = layerSet.artLayers[i];
|
||||||
|
|
||||||
|
// 在这里执行对每个图层的操作
|
||||||
|
// 例如,打印图层的名字
|
||||||
|
alert("图层名: " + layer.name);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("发生错误或找不到图层组: " + e);
|
||||||
|
}
|
||||||
16
PSMARK代码块/拼合所有文档.jsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
function 删除图层3()
|
||||||
|
{
|
||||||
|
app.activeDocument.suspendHistory("Combine all masks", "拼合所有蒙版()");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 拼合所有蒙版() //拼合所有蒙版
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
executeAction(stringIDToTypeID("e805a6ee-6d75-4b62-b6fe-f5873b5fdf20"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
8
PSMARK代码块/新的名称还回.jsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
function 名称重新赋予(layername,angle,size) //
|
||||||
|
{
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
var selectedLayer = doc.activeLayer;
|
||||||
|
selectedLayer.name =layername+"-"+"Mark"+"_"+angle+"_"+size
|
||||||
|
}
|
||||||
|
//layername+"-"+"Mark"+"_"+angle+"_"+size
|
||||||
55
PSMARK代码块/新的图案填充.jsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
// 找到图层组后,显示文件夹选择框
|
||||||
|
var 文件夹 = Folder.selectDialog("请选择要打开文件的文件夹");
|
||||||
|
|
||||||
|
if (文件夹) {
|
||||||
|
var 文件列表 = 文件夹.getFiles();
|
||||||
|
for (var i = 0; i < 文件列表.length; i++) {
|
||||||
|
var 文件 = 文件列表[i];
|
||||||
|
// 确保文件是图像文件
|
||||||
|
if (文件 instanceof File && 文件.name.match(/\.(jpg|jpeg|png|gif|psd)$/i)) {
|
||||||
|
原始文档 = app.activeDocument;
|
||||||
|
app.open(文件);
|
||||||
|
图像大小();
|
||||||
|
预设图案(当前文档名称);
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
app.activeDocument =原始文档;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 图像大小() //图像大小
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), 200);
|
||||||
|
d.putBoolean(stringIDToTypeID("scaleStyles"), true);
|
||||||
|
d.putBoolean(stringIDToTypeID("constrainProportions"), true);
|
||||||
|
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||||
|
executeAction(stringIDToTypeID("imageSize"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 预设图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("pattern"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||||
|
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
45
PSMARK代码块/新的排序图层顺序改名.jsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
|
||||||
|
function 图层排序改名(){
|
||||||
|
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
|
||||||
|
// 函数:获取图层的面积
|
||||||
|
function getLayerArea(layer) {
|
||||||
|
var bounds = layer.bounds;
|
||||||
|
var width = bounds[2].value - bounds[0].value;
|
||||||
|
var height = bounds[3].value - bounds[1].value;
|
||||||
|
return width * height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个包含所有图层及其面积的数组
|
||||||
|
var layers = [];
|
||||||
|
for (var i = 0; i < doc.layers.length; i++) {
|
||||||
|
var layer = doc.layers[i];
|
||||||
|
var area = getLayerArea(layer);
|
||||||
|
layers.push({ layer: layer, area: area });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按面积对图层进行排序
|
||||||
|
layers.sort(function(a, b) {
|
||||||
|
return b.area - a.area; // 从大到小排序
|
||||||
|
});
|
||||||
|
|
||||||
|
// 正则表达式,用于移除图层名称中的最后一个字符
|
||||||
|
var regex = /(.+)-\d+$/;
|
||||||
|
|
||||||
|
// 重新排列图层,并重命名
|
||||||
|
for (var i = 0; i < layers.length; i++) {
|
||||||
|
// 移动图层到文档顶部
|
||||||
|
layers[i].layer.move(doc, ElementPlacement.PLACEATBEGINNING);
|
||||||
|
|
||||||
|
// 获取原始图层名称,并使用正则表达式处理
|
||||||
|
var originalName = layers[i].layer.name;
|
||||||
|
var newName = originalName.replace(regex, "$1");
|
||||||
|
|
||||||
|
// 添加新的排序编号
|
||||||
|
layers[i].layer.name = newName + "-" + (i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
dxf26_jscode = """
|
// 弹出文件夹选择框
|
||||||
// 弹出文件夹选择框
|
模特换图()
|
||||||
|
|
||||||
function 模特换图(){
|
function 模特换图(){
|
||||||
建立快照()
|
建立快照()
|
||||||
var folder = Folder.selectDialog("请选择一个文件夹");
|
var folder = Folder.selectDialog("请选择一个文件夹");
|
||||||
@@ -63,7 +62,7 @@ if (folder) {
|
|||||||
// 关闭当前文档
|
// 关闭当前文档
|
||||||
历史记录回退到快照1()
|
历史记录回退到快照1()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert("无法打开文件: " );
|
alert("无法打开文件: " + file.name + "\n错误信息: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -215,4 +214,3 @@ function 建立快照() //打开
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
|
||||||
363
PSMARK代码块/新的米样拼贴(11.15).jsx
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
// 设置单位为像素
|
||||||
|
自动米样拼贴()
|
||||||
|
function 自动米样拼贴(){
|
||||||
|
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
|
||||||
|
|
||||||
|
dialog.text = "SO自动米样拼贴";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "源图像";
|
||||||
|
panel1.preferredSize.width = 388;
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "使用:";
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "row";
|
||||||
|
group2.alignChildren = ["left","center"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "选取";
|
||||||
|
|
||||||
|
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||||
|
edittext1.text = "[选择图像文件夹]";
|
||||||
|
edittext1.preferredSize.width = 300;
|
||||||
|
|
||||||
|
// PANEL2
|
||||||
|
// ======
|
||||||
|
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||||
|
panel2.text = "SO小样文档";
|
||||||
|
panel2.preferredSize.height = 160;
|
||||||
|
panel2.orientation = "column";
|
||||||
|
panel2.alignChildren = ["left","top"];
|
||||||
|
panel2.spacing = 10;
|
||||||
|
panel2.margins = 10;
|
||||||
|
|
||||||
|
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "设定:";
|
||||||
|
|
||||||
|
// GROUP3
|
||||||
|
// ======
|
||||||
|
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||||
|
group3.orientation = "row";
|
||||||
|
group3.alignChildren = ["left","center"];
|
||||||
|
group3.spacing = 10;
|
||||||
|
group3.margins = 0;
|
||||||
|
|
||||||
|
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "幅宽(cm):";
|
||||||
|
|
||||||
|
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||||
|
edittext2.preferredSize.width = 100;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP4
|
||||||
|
// ======
|
||||||
|
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||||
|
group4.orientation = "row";
|
||||||
|
group4.alignChildren = ["left","center"];
|
||||||
|
group4.spacing = 10;
|
||||||
|
group4.margins = 0;
|
||||||
|
|
||||||
|
// GROUP5
|
||||||
|
// ======
|
||||||
|
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||||
|
group5.orientation = "row";
|
||||||
|
group5.alignChildren = ["left","center"];
|
||||||
|
group5.spacing = 10;
|
||||||
|
group5.margins = 0;
|
||||||
|
|
||||||
|
// PANEL3
|
||||||
|
// ======
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP7
|
||||||
|
// ======
|
||||||
|
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||||
|
group7.orientation = "column";
|
||||||
|
group7.alignChildren = ["fill","top"];
|
||||||
|
group7.spacing = 10;
|
||||||
|
group7.margins = 0;
|
||||||
|
|
||||||
|
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "确认";
|
||||||
|
|
||||||
|
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
dialog.text = "SO自动米样拼贴";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "源图像";
|
||||||
|
panel1.preferredSize.width = 388;
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "使用:";
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "row";
|
||||||
|
group2.alignChildren = ["left","center"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "选取";
|
||||||
|
|
||||||
|
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||||
|
edittext1.text = "[选择图像文件夹]";
|
||||||
|
edittext1.preferredSize.width = 300;
|
||||||
|
|
||||||
|
// PANEL2
|
||||||
|
// ======
|
||||||
|
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||||
|
panel2.text = "SO小样文档拼贴";
|
||||||
|
panel2.preferredSize.height = 160;
|
||||||
|
panel2.orientation = "column";
|
||||||
|
panel2.alignChildren = ["left","top"];
|
||||||
|
panel2.spacing = 10;
|
||||||
|
panel2.margins = 10;
|
||||||
|
|
||||||
|
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "设定:";
|
||||||
|
|
||||||
|
// GROUP3
|
||||||
|
// ======
|
||||||
|
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||||
|
group3.orientation = "row";
|
||||||
|
group3.alignChildren = ["left","center"];
|
||||||
|
group3.spacing = 10;
|
||||||
|
group3.margins = 0;
|
||||||
|
|
||||||
|
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "幅宽(cm):";
|
||||||
|
|
||||||
|
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||||
|
edittext2.preferredSize.width = 100;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP4
|
||||||
|
// ======
|
||||||
|
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||||
|
group4.orientation = "row";
|
||||||
|
group4.alignChildren = ["left","center"];
|
||||||
|
group4.spacing = 10;
|
||||||
|
group4.margins = 0;
|
||||||
|
|
||||||
|
// GROUP5
|
||||||
|
// ======
|
||||||
|
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||||
|
group5.orientation = "row";
|
||||||
|
group5.alignChildren = ["left","center"];
|
||||||
|
group5.spacing = 10;
|
||||||
|
group5.margins = 0;
|
||||||
|
|
||||||
|
// PANEL3
|
||||||
|
// ======
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP7
|
||||||
|
// ======
|
||||||
|
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||||
|
group7.orientation = "column";
|
||||||
|
group7.alignChildren = ["fill","top"];
|
||||||
|
group7.spacing = 10;
|
||||||
|
group7.margins = 0;
|
||||||
|
|
||||||
|
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "确认";
|
||||||
|
|
||||||
|
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
button1.onClick = function () {
|
||||||
|
// 打开文件夹选择对话框
|
||||||
|
var selectedFolder = Folder.selectDialog("选择图像文件夹");
|
||||||
|
|
||||||
|
// 检查用户是否取消了选择
|
||||||
|
if (selectedFolder) {
|
||||||
|
// 将选择的文件夹路径显示在输入框中
|
||||||
|
edittext1.text = selectedFolder.fsName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ok.onClick = function () {
|
||||||
|
app.preferences.rulerUnits = Units.PIXELS;
|
||||||
|
//function 创建拼贴图(导入文件夹路径, 文档宽度厘米) {
|
||||||
|
|
||||||
|
// 假设这些值是从某处获取或者用户输入的
|
||||||
|
var 导入文件夹路径 =new Folder (edittext1.text);
|
||||||
|
var 文档宽度厘米 = Number(edittext2.text);
|
||||||
|
var 文档高度厘米 = 6000; // 例如,50厘米
|
||||||
|
var 分辨率 = 150; // 分辨率设置为150 DPI
|
||||||
|
|
||||||
|
// 转换厘米到像素
|
||||||
|
var 厘米到像素的转换系数 = 2.54;
|
||||||
|
var 文档宽度像素 = 文档宽度厘米 / 厘米到像素的转换系数 * 分辨率;
|
||||||
|
var 文档高度像素 = 文档高度厘米 / 厘米到像素的转换系数 * 分辨率;
|
||||||
|
|
||||||
|
var 导入文件夹 = new Folder(导入文件夹路径);
|
||||||
|
var 文件列表 = 导入文件夹.getFiles("*.*");
|
||||||
|
var 文件名数组 = new Array();
|
||||||
|
var 文件宽度数组 = new Array();
|
||||||
|
var 文件高度数组 = new Array();
|
||||||
|
|
||||||
|
|
||||||
|
// 读取文件列表并获取文件信息
|
||||||
|
for (var 索引1 = 0; 索引1 < 文件列表.length; 索引1++) {
|
||||||
|
var 文档 = app.open(文件列表[索引1]);
|
||||||
|
文件名数组[索引1] = 文档.fullName;
|
||||||
|
文件宽度数组[索引1] = parseInt(文档.width);
|
||||||
|
文件高度数组[索引1] = parseInt(文档.height);
|
||||||
|
文档.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据高度对文件进行排序
|
||||||
|
for (var 索引1 = 文件高度数组.length - 1; 索引1 > 0; --索引1) {
|
||||||
|
for (var 索引2 = 0; 索引2 < 索引1; ++索引2) {
|
||||||
|
if (文件高度数组[索引2] > 文件高度数组[索引2 + 1])
|
||||||
|
交换数组元素(索引2, 索引2 + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function 交换数组元素(索引1, 索引2) {
|
||||||
|
var 临时 = 文件高度数组[索引1];
|
||||||
|
文件高度数组[索引1] = 文件高度数组[索引2];
|
||||||
|
文件高度数组[索引2] = 临时;
|
||||||
|
|
||||||
|
var 临时2 = 文件宽度数组[索引1];
|
||||||
|
文件宽度数组[索引1] = 文件宽度数组[索引2];
|
||||||
|
文件宽度数组[索引2] = 临时2;
|
||||||
|
|
||||||
|
var 临时3 = 文件名数组[索引1];
|
||||||
|
文件名数组[索引1] = 文件名数组[索引2];
|
||||||
|
文件名数组[索引2] = 临时3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新文档并添加图片
|
||||||
|
var 新文档 = app.documents.add(文档宽度像素, 文档高度像素, 分辨率, "拼贴", NewDocumentMode.CMYK, DocumentFill.WHITE);
|
||||||
|
var 当前顶部 = 0;
|
||||||
|
var 当前左侧 = 0;
|
||||||
|
for (var 索引1 = 0; 索引1 < 文件名数组.length; 索引1++) {
|
||||||
|
var 文档 = app.open(文件名数组[索引1]);
|
||||||
|
文档.selection.selectAll();
|
||||||
|
文档.selection.copy();
|
||||||
|
文档.close();
|
||||||
|
|
||||||
|
if ((当前左侧 + 文件宽度数组[索引1]) > 文档宽度像素) {
|
||||||
|
当前左侧 = 0;
|
||||||
|
当前顶部 += 文件高度数组[索引1 - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var 边界 = [
|
||||||
|
[当前左侧, 当前顶部],
|
||||||
|
[当前左侧 + 文件宽度数组[索引1], 当前顶部],
|
||||||
|
[当前左侧 + 文件宽度数组[索引1], 当前顶部 + 文件高度数组[索引1]],
|
||||||
|
[当前左侧, 当前顶部 + 文件高度数组[索引1]]
|
||||||
|
];
|
||||||
|
新文档.selection.select(边界, SelectionType.REPLACE, 0, true);
|
||||||
|
新文档.paste();
|
||||||
|
当前左侧 += 文件宽度数组[索引1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择输出文件夹并保存图片
|
||||||
|
var 输出文件夹 = new Folder(edittext1.text + "/拼贴");
|
||||||
|
if (!输出文件夹.exists) 输出文件夹.create();
|
||||||
|
|
||||||
|
// 设置TIFF保存选项
|
||||||
|
var tiffSaveOptions = new TiffSaveOptions();
|
||||||
|
tiffSaveOptions.imageCompression = TIFFEncoding.TIFFLZW; // 使用LZW压缩
|
||||||
|
tiffSaveOptions.byteOrder = ByteOrder.IBM; // 设置字节顺序为IBM(大端序)
|
||||||
|
|
||||||
|
// 定义保存的文件路径和名称
|
||||||
|
var 文件 = new File(输出文件夹 + "/combined.tif");
|
||||||
|
|
||||||
|
// 保存当前文档为TIFF格式
|
||||||
|
新文档.saveAs(文件, tiffSaveOptions, true, Extension.LOWERCASE);
|
||||||
|
裁切透明像素();
|
||||||
|
|
||||||
|
// 关闭文档,不保存更改
|
||||||
|
//新文档.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
dialog.close();
|
||||||
|
alert("拼贴完成")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 裁切透明像素() {
|
||||||
|
var 文档 = app.activeDocument; // 获取当前活动的文档
|
||||||
|
|
||||||
|
// 裁切透明像素
|
||||||
|
// TrimType.TOPLEFT 表示从图像的左上角开始裁切
|
||||||
|
// 第二个参数 true 表示裁切顶部的透明像素
|
||||||
|
// 第三个参数 true 表示裁切左侧的透明像素
|
||||||
|
// 第四个参数 true 表示裁切底部的透明像素
|
||||||
|
// 第五个参数 true 表示裁切右侧的透明像素
|
||||||
|
文档.trim(TrimType.TOPLEFT, true, true, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
}
|
||||||
459
PSMARK代码块/新的米样拼贴.jsx
Normal file
@@ -0,0 +1,459 @@
|
|||||||
|
|
||||||
|
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
|
||||||
|
dialog.text = "S/O样自动连晒";
|
||||||
|
//dialog.text = "SO自动米样拼贴";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "源图像";
|
||||||
|
panel1.preferredSize.width = 388;
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "使用:";
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "row";
|
||||||
|
group2.alignChildren = ["left","center"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "选取";
|
||||||
|
|
||||||
|
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||||
|
edittext1.text = "[选择图像文件夹]";
|
||||||
|
edittext1.preferredSize.width = 300;
|
||||||
|
|
||||||
|
// PANEL2
|
||||||
|
// ======
|
||||||
|
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||||
|
panel2.text = "SO小样文档";
|
||||||
|
panel2.preferredSize.height = 160;
|
||||||
|
panel2.orientation = "column";
|
||||||
|
panel2.alignChildren = ["left","top"];
|
||||||
|
panel2.spacing = 10;
|
||||||
|
panel2.margins = 10;
|
||||||
|
|
||||||
|
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "设定:";
|
||||||
|
|
||||||
|
// GROUP3
|
||||||
|
// ======
|
||||||
|
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||||
|
group3.orientation = "row";
|
||||||
|
group3.alignChildren = ["left","center"];
|
||||||
|
group3.spacing = 10;
|
||||||
|
group3.margins = 0;
|
||||||
|
|
||||||
|
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "宽度(cm):";
|
||||||
|
|
||||||
|
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||||
|
edittext2.preferredSize.width = 100;
|
||||||
|
|
||||||
|
var statictext4 = group3.add("statictext", undefined, undefined, {name: "statictext4"});
|
||||||
|
statictext4.text = "高度(cm):";
|
||||||
|
|
||||||
|
var edittext3 = group3.add('edittext {properties: {name: "edittext3"}}');
|
||||||
|
edittext3.preferredSize.width = 100;
|
||||||
|
|
||||||
|
// GROUP4
|
||||||
|
// ======
|
||||||
|
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||||
|
group4.orientation = "row";
|
||||||
|
group4.alignChildren = ["left","center"];
|
||||||
|
group4.spacing = 10;
|
||||||
|
group4.margins = 0;
|
||||||
|
|
||||||
|
// GROUP5
|
||||||
|
// ======
|
||||||
|
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||||
|
group5.orientation = "row";
|
||||||
|
group5.alignChildren = ["left","center"];
|
||||||
|
group5.spacing = 10;
|
||||||
|
group5.margins = 0;
|
||||||
|
|
||||||
|
// PANEL3
|
||||||
|
// ======
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP7
|
||||||
|
// ======
|
||||||
|
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||||
|
group7.orientation = "column";
|
||||||
|
group7.alignChildren = ["fill","top"];
|
||||||
|
group7.spacing = 10;
|
||||||
|
group7.margins = 0;
|
||||||
|
|
||||||
|
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "确认";
|
||||||
|
|
||||||
|
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
button1.onClick = function () {
|
||||||
|
// 打开文件夹选择对话框
|
||||||
|
var selectedFolder = Folder.selectDialog("选择图像文件夹");
|
||||||
|
|
||||||
|
// 检查用户是否取消了选择
|
||||||
|
if (selectedFolder) {
|
||||||
|
// 将选择的文件夹路径显示在输入框中
|
||||||
|
edittext1.text = selectedFolder.fsName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ok.onClick = function () {
|
||||||
|
app.preferences.rulerUnits = Units.PIXELS;
|
||||||
|
var 素材图片文件夹 = new Folder(edittext1.text);
|
||||||
|
var 遍历tiff = 素材图片文件夹.getFiles("*.*");
|
||||||
|
|
||||||
|
// 新建文件夹
|
||||||
|
var 新文件夹 = new Folder(edittext1.text + "/SO小样拼贴");
|
||||||
|
新文件夹.create();
|
||||||
|
var 新加字文件夹 = new Folder(edittext1.text + "/SO小样拼贴加字");
|
||||||
|
新加字文件夹.create();
|
||||||
|
|
||||||
|
|
||||||
|
宽度=Number (edittext2.text);
|
||||||
|
高度=Number (edittext3.text);
|
||||||
|
|
||||||
|
|
||||||
|
for (var i = 0; i < 遍历tiff.length; i++) {
|
||||||
|
if (遍历tiff[i] instanceof File) {
|
||||||
|
app.open(遍历tiff[i]);
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
var 当前文档 = app.activeDocument;
|
||||||
|
var 当前文档名称 = 当前文档.name;
|
||||||
|
图像大小()
|
||||||
|
预设图案(当前文档名称);
|
||||||
|
app.activeDocument.crop([UnitValue("0px"), UnitValue("0px"), UnitValue(宽度, "cm"), UnitValue(高度, "cm")]);
|
||||||
|
填充图案(当前文档名称);
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
画布扩展()
|
||||||
|
// 保存新的图像文件到新建的文件夹中
|
||||||
|
var 保存路径 = 新文件夹 + "/" + 当前文档名称;
|
||||||
|
var tifSaveOpt = new TiffSaveOptions();
|
||||||
|
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||||
|
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||||
|
var asCopy = true;
|
||||||
|
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||||
|
创建并处理文本图层();
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
var 保存路径 = 新加字文件夹 + "/" + 当前文档名称;
|
||||||
|
var tifSaveOpt = new TiffSaveOptions();
|
||||||
|
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||||
|
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||||
|
var asCopy = true;
|
||||||
|
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alert("小样连晒完成")
|
||||||
|
// 创建并保存拼贴图像(新文件夹, 幅宽)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 预设图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("pattern"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||||
|
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 填充图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("contentLayer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
var d2 = new ActionDescriptor();
|
||||||
|
var d3 = new ActionDescriptor();
|
||||||
|
d3.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
|
||||||
|
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||||
|
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 图像大小()
|
||||||
|
{
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), 150);
|
||||||
|
d.putBoolean(stringIDToTypeID("scaleStyles"), true);
|
||||||
|
d.putBoolean(stringIDToTypeID("constrainProportions"), true);
|
||||||
|
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("automaticInterpolation"));
|
||||||
|
executeAction(stringIDToTypeID("imageSize"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 画布扩展() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||||
|
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||||
|
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||||
|
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||||
|
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||||
|
d.putEnumerated(stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("color"));
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putDouble(stringIDToTypeID("red"), 255);
|
||||||
|
d1.putDouble(stringIDToTypeID("green"), 255);
|
||||||
|
d1.putDouble(stringIDToTypeID("blue"), 255);
|
||||||
|
d.putObject(stringIDToTypeID("canvasExtensionColor"), stringIDToTypeID("RGBColor"), d1);
|
||||||
|
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 创建并处理文本图层() {
|
||||||
|
// 新建图层
|
||||||
|
var textLayer = activeDocument.artLayers.add();
|
||||||
|
|
||||||
|
// 将新建图层变成文本图层
|
||||||
|
textLayer.kind = LayerKind.TEXT;
|
||||||
|
|
||||||
|
// 将文本内容改为当前文档name
|
||||||
|
textLayer.textItem.contents = activeDocument.name;
|
||||||
|
|
||||||
|
// 字体大小固定值
|
||||||
|
var 固定字体大小 = 30; // 例如,30像素
|
||||||
|
textLayer.textItem.size = 固定字体大小;
|
||||||
|
|
||||||
|
// 文字字体固定值
|
||||||
|
textLayer.textItem.font = "微软雅黑";
|
||||||
|
|
||||||
|
// 计算并调整文本位置
|
||||||
|
var x = activeDocument.width - textLayer.bounds[2];
|
||||||
|
var y = textLayer.bounds[1];
|
||||||
|
textLayer.translate(x, -y);
|
||||||
|
|
||||||
|
// 偏移
|
||||||
|
textLayer.translate(UnitValue("-1cm"), UnitValue("+0.5cm"));
|
||||||
|
|
||||||
|
// 复制并栅格化图层
|
||||||
|
var copyLayer = textLayer.duplicate();
|
||||||
|
copyLayer.rasterize(RasterizeType.ENTIRELAYER);
|
||||||
|
|
||||||
|
// 设置固定颜色值
|
||||||
|
var 固定颜色 = new SolidColor();
|
||||||
|
固定颜色.rgb.red = 255; // 红色分量
|
||||||
|
固定颜色.rgb.green = 255; // 绿色分量
|
||||||
|
固定颜色.rgb.blue = 255; // 蓝色分量
|
||||||
|
|
||||||
|
activeDocument.activeLayer = copyLayer;
|
||||||
|
activeDocument.selection.fill(固定颜色, ColorBlendMode.NORMAL, 100, true);
|
||||||
|
|
||||||
|
// 白边
|
||||||
|
activeDocument.activeLayer.applyMinimum(10);
|
||||||
|
后移一层()
|
||||||
|
向上选择()
|
||||||
|
向下合并()
|
||||||
|
名称更改字体()
|
||||||
|
缩小字体图层至文档一半()
|
||||||
|
多选背景()
|
||||||
|
底对齐()
|
||||||
|
水平居中对齐()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 后移一层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 向上选择() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(19);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
function 向下合并() //向下合并
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 名称更改字体() //名称更改
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putString(stringIDToTypeID("name"), "字体");
|
||||||
|
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 缩小字体图层至文档一半() {
|
||||||
|
var 文档 = app.activeDocument;
|
||||||
|
var 字体图层 = null;
|
||||||
|
|
||||||
|
// 遍历文档中的图层以找到名为"字体"的图层
|
||||||
|
for (var i = 0; i < 文档.artLayers.length; i++) {
|
||||||
|
if (文档.artLayers[i].name === "字体") {
|
||||||
|
字体图层 = 文档.artLayers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (字体图层 !== null) {
|
||||||
|
// 获取文档的宽度的一半
|
||||||
|
var 目标宽度 = 文档.width / 2;
|
||||||
|
|
||||||
|
// 获取图层的当前宽度
|
||||||
|
var 图层宽度 = 字体图层.bounds[2] - 字体图层.bounds[0];
|
||||||
|
|
||||||
|
// 计算缩放比例
|
||||||
|
var 缩放比例 = 目标宽度 / 图层宽度 * 100;
|
||||||
|
|
||||||
|
// 缩放图层
|
||||||
|
字体图层.resize(缩放比例, 缩放比例, AnchorPosition.MIDDLECENTER);
|
||||||
|
} else {
|
||||||
|
alert("未找到名为'字体'的图层");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 多选背景() //自由变换
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "背景");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(1);
|
||||||
|
list.putInteger(13);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 底对齐() //底对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSBottoms"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 水平居中对齐() //水平居中对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSCentersH"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 设置单位为像素
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
489
PSMARK代码块/新的米样缩放.jsx
Normal file
@@ -0,0 +1,489 @@
|
|||||||
|
新的米样缩放()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 新的米样缩放()
|
||||||
|
{
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
|
||||||
|
dialog.text = "S/O样自动缩放";
|
||||||
|
//dialog.text = "SO自动米样拼贴";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "源图像";
|
||||||
|
panel1.preferredSize.width = 388;
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "使用:";
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "row";
|
||||||
|
group2.alignChildren = ["left","center"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "选取";
|
||||||
|
|
||||||
|
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||||
|
edittext1.text = "[选择图像文件夹]";
|
||||||
|
edittext1.preferredSize.width = 300;
|
||||||
|
|
||||||
|
// PANEL2
|
||||||
|
// ======
|
||||||
|
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||||
|
panel2.text = "SO小样文档";
|
||||||
|
panel2.preferredSize.height = 160;
|
||||||
|
panel2.orientation = "column";
|
||||||
|
panel2.alignChildren = ["left","top"];
|
||||||
|
panel2.spacing = 10;
|
||||||
|
panel2.margins = 10;
|
||||||
|
|
||||||
|
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "设定:";
|
||||||
|
|
||||||
|
// GROUP3
|
||||||
|
// ======
|
||||||
|
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||||
|
group3.orientation = "row";
|
||||||
|
group3.alignChildren = ["left","center"];
|
||||||
|
group3.spacing = 10;
|
||||||
|
group3.margins = 0;
|
||||||
|
|
||||||
|
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "宽度(cm):";
|
||||||
|
|
||||||
|
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||||
|
edittext2.preferredSize.width = 100;
|
||||||
|
|
||||||
|
var statictext4 = group3.add("statictext", undefined, undefined, {name: "statictext4"});
|
||||||
|
statictext4.text = "高度(cm):";
|
||||||
|
|
||||||
|
var edittext3 = group3.add('edittext {properties: {name: "edittext3"}}');
|
||||||
|
edittext3.preferredSize.width = 100;
|
||||||
|
|
||||||
|
// GROUP4
|
||||||
|
// ======
|
||||||
|
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||||
|
group4.orientation = "row";
|
||||||
|
group4.alignChildren = ["left","center"];
|
||||||
|
group4.spacing = 10;
|
||||||
|
group4.margins = 0;
|
||||||
|
|
||||||
|
// GROUP5
|
||||||
|
// ======
|
||||||
|
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||||
|
group5.orientation = "row";
|
||||||
|
group5.alignChildren = ["left","center"];
|
||||||
|
group5.spacing = 10;
|
||||||
|
group5.margins = 0;
|
||||||
|
|
||||||
|
// PANEL3
|
||||||
|
// ======
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP7
|
||||||
|
// ======
|
||||||
|
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||||
|
group7.orientation = "column";
|
||||||
|
group7.alignChildren = ["fill","top"];
|
||||||
|
group7.spacing = 10;
|
||||||
|
group7.margins = 0;
|
||||||
|
|
||||||
|
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "确认";
|
||||||
|
|
||||||
|
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
button1.onClick = function () {
|
||||||
|
// 打开文件夹选择对话框
|
||||||
|
var selectedFolder = Folder.selectDialog("选择图像文件夹");
|
||||||
|
|
||||||
|
// 检查用户是否取消了选择
|
||||||
|
if (selectedFolder) {
|
||||||
|
// 将选择的文件夹路径显示在输入框中
|
||||||
|
edittext1.text = selectedFolder.fsName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ok.onClick = function () {
|
||||||
|
app.preferences.rulerUnits = Units.PIXELS;
|
||||||
|
var 素材图片文件夹 = new Folder(edittext1.text);
|
||||||
|
var 遍历tiff = 素材图片文件夹.getFiles("*.*");
|
||||||
|
|
||||||
|
// 新建文件夹
|
||||||
|
var 新文件夹 = new Folder(edittext1.text + "/SO小样拼贴");
|
||||||
|
新文件夹.create();
|
||||||
|
var 新加字文件夹 = new Folder(edittext1.text + "/SO小样拼贴加字");
|
||||||
|
新加字文件夹.create();
|
||||||
|
|
||||||
|
|
||||||
|
宽度=Number (edittext2.text);
|
||||||
|
高度=Number (edittext3.text);
|
||||||
|
|
||||||
|
|
||||||
|
for (var i = 0; i < 遍历tiff.length; i++) {
|
||||||
|
if (遍历tiff[i] instanceof File) {
|
||||||
|
app.open(遍历tiff[i]);
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
var 当前文档 = app.activeDocument;
|
||||||
|
var 当前文档名称 = 当前文档.name;
|
||||||
|
resizeImageToCm(宽度, 高度,200);
|
||||||
|
/*
|
||||||
|
图像大小()
|
||||||
|
预设图案(当前文档名称);
|
||||||
|
app.activeDocument.crop([UnitValue("0px"), UnitValue("0px"), UnitValue(宽度, "cm"), UnitValue(高度, "cm")]);
|
||||||
|
填充图案(当前文档名称);
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
画布扩展()
|
||||||
|
*/
|
||||||
|
// 保存新的图像文件到新建的文件夹中
|
||||||
|
var 保存路径 = 新文件夹 + "/" + 当前文档名称;
|
||||||
|
var tifSaveOpt = new TiffSaveOptions();
|
||||||
|
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||||
|
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||||
|
var asCopy = true;
|
||||||
|
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||||
|
创建并处理文本图层();
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
var 保存路径 = 新加字文件夹 + "/" + 当前文档名称;
|
||||||
|
var tifSaveOpt = new TiffSaveOptions();
|
||||||
|
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||||
|
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||||
|
var asCopy = true;
|
||||||
|
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//alert("小样连晒完成")
|
||||||
|
dialog.close();
|
||||||
|
// 创建并保存拼贴图像(新文件夹, 幅宽)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function resizeImageToCm(widthCm, heightCm, resolution) {
|
||||||
|
// 将厘米转换为像素
|
||||||
|
var widthInPixels = widthCm * (resolution / 2.54);
|
||||||
|
var heightInPixels = heightCm * (resolution / 2.54);
|
||||||
|
|
||||||
|
// 调整图像大小
|
||||||
|
app.activeDocument.resizeImage(widthInPixels, heightInPixels, resolution, ResampleMethod.NEARESTNEIGHBOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例:将图像大小调整为 10cm x 15cm,分辨率为 300 像素/英寸
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 预设图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("pattern"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||||
|
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 填充图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("contentLayer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
var d2 = new ActionDescriptor();
|
||||||
|
var d3 = new ActionDescriptor();
|
||||||
|
d3.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
|
||||||
|
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||||
|
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 图像大小() //图像大小
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), 200);
|
||||||
|
d.putBoolean(stringIDToTypeID("scaleStyles"), true);
|
||||||
|
d.putBoolean(stringIDToTypeID("constrainProportions"), true);
|
||||||
|
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||||
|
executeAction(stringIDToTypeID("imageSize"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 画布扩展() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||||
|
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||||
|
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||||
|
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||||
|
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||||
|
d.putEnumerated(stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("color"));
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putDouble(stringIDToTypeID("red"), 255);
|
||||||
|
d1.putDouble(stringIDToTypeID("green"), 255);
|
||||||
|
d1.putDouble(stringIDToTypeID("blue"), 255);
|
||||||
|
d.putObject(stringIDToTypeID("canvasExtensionColor"), stringIDToTypeID("RGBColor"), d1);
|
||||||
|
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 创建并处理文本图层() {
|
||||||
|
// 新建图层
|
||||||
|
var textLayer = activeDocument.artLayers.add();
|
||||||
|
|
||||||
|
// 将新建图层变成文本图层
|
||||||
|
textLayer.kind = LayerKind.TEXT;
|
||||||
|
|
||||||
|
// 将文本内容改为当前文档name
|
||||||
|
textLayer.textItem.contents = activeDocument.name;
|
||||||
|
|
||||||
|
// 字体大小固定值
|
||||||
|
var 固定字体大小 = 30; // 例如,30像素
|
||||||
|
textLayer.textItem.size = 固定字体大小;
|
||||||
|
|
||||||
|
// 文字字体固定值
|
||||||
|
textLayer.textItem.font = "微软雅黑";
|
||||||
|
|
||||||
|
// 计算并调整文本位置
|
||||||
|
var x = activeDocument.width - textLayer.bounds[2];
|
||||||
|
var y = textLayer.bounds[1];
|
||||||
|
textLayer.translate(x, -y);
|
||||||
|
|
||||||
|
// 偏移
|
||||||
|
textLayer.translate(UnitValue("-1cm"), UnitValue("+0.5cm"));
|
||||||
|
|
||||||
|
// 复制并栅格化图层
|
||||||
|
var copyLayer = textLayer.duplicate();
|
||||||
|
copyLayer.rasterize(RasterizeType.ENTIRELAYER);
|
||||||
|
|
||||||
|
// 设置固定颜色值
|
||||||
|
var 固定颜色 = new SolidColor();
|
||||||
|
固定颜色.rgb.red = 255; // 红色分量
|
||||||
|
固定颜色.rgb.green = 255; // 绿色分量
|
||||||
|
固定颜色.rgb.blue = 255; // 蓝色分量
|
||||||
|
|
||||||
|
activeDocument.activeLayer = copyLayer;
|
||||||
|
activeDocument.selection.fill(固定颜色, ColorBlendMode.NORMAL, 100, true);
|
||||||
|
|
||||||
|
// 白边
|
||||||
|
activeDocument.activeLayer.applyMinimum(10);
|
||||||
|
后移一层()
|
||||||
|
向上选择()
|
||||||
|
向下合并()
|
||||||
|
名称更改字体()
|
||||||
|
缩小字体图层至文档一半()
|
||||||
|
多选背景()
|
||||||
|
底对齐()
|
||||||
|
水平居中对齐()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 后移一层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 向上选择() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(19);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
function 向下合并() //向下合并
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 名称更改字体() //名称更改
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putString(stringIDToTypeID("name"), "字体");
|
||||||
|
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 缩小字体图层至文档一半() {
|
||||||
|
var 文档 = app.activeDocument;
|
||||||
|
var 字体图层 = null;
|
||||||
|
|
||||||
|
// 遍历文档中的图层以找到名为"字体"的图层
|
||||||
|
for (var i = 0; i < 文档.artLayers.length; i++) {
|
||||||
|
if (文档.artLayers[i].name === "字体") {
|
||||||
|
字体图层 = 文档.artLayers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (字体图层 !== null) {
|
||||||
|
// 获取文档的宽度的一半
|
||||||
|
var 目标宽度 = 文档.width / 2;
|
||||||
|
|
||||||
|
// 获取图层的当前宽度
|
||||||
|
var 图层宽度 = 字体图层.bounds[2] - 字体图层.bounds[0];
|
||||||
|
|
||||||
|
// 计算缩放比例
|
||||||
|
var 缩放比例 = 目标宽度 / 图层宽度 * 100;
|
||||||
|
|
||||||
|
// 缩放图层
|
||||||
|
字体图层.resize(缩放比例, 缩放比例, AnchorPosition.MIDDLECENTER);
|
||||||
|
} else {
|
||||||
|
alert("未找到名为'字体'的图层");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 多选背景() //自由变换
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "背景");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(1);
|
||||||
|
list.putInteger(13);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 底对齐() //底对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSBottoms"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 水平居中对齐() //水平居中对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSCentersH"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 设置单位为像素
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
461
PSMARK代码块/新的米样连晒.jsx
Normal file
@@ -0,0 +1,461 @@
|
|||||||
|
|
||||||
|
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
|
||||||
|
dialog.text = "S/O样自动连晒";
|
||||||
|
//dialog.text = "SO自动米样拼贴";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "源图像";
|
||||||
|
panel1.preferredSize.width = 388;
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "使用:";
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "row";
|
||||||
|
group2.alignChildren = ["left","center"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "选取";
|
||||||
|
|
||||||
|
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||||
|
edittext1.text = "[选择图像文件夹]";
|
||||||
|
edittext1.preferredSize.width = 300;
|
||||||
|
|
||||||
|
// PANEL2
|
||||||
|
// ======
|
||||||
|
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||||
|
panel2.text = "SO小样文档";
|
||||||
|
panel2.preferredSize.height = 160;
|
||||||
|
panel2.orientation = "column";
|
||||||
|
panel2.alignChildren = ["left","top"];
|
||||||
|
panel2.spacing = 10;
|
||||||
|
panel2.margins = 10;
|
||||||
|
|
||||||
|
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "设定:";
|
||||||
|
|
||||||
|
// GROUP3
|
||||||
|
// ======
|
||||||
|
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||||
|
group3.orientation = "row";
|
||||||
|
group3.alignChildren = ["left","center"];
|
||||||
|
group3.spacing = 10;
|
||||||
|
group3.margins = 0;
|
||||||
|
|
||||||
|
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "宽度(cm):";
|
||||||
|
|
||||||
|
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||||
|
edittext2.preferredSize.width = 100;
|
||||||
|
|
||||||
|
var statictext4 = group3.add("statictext", undefined, undefined, {name: "statictext4"});
|
||||||
|
statictext4.text = "高度(cm):";
|
||||||
|
|
||||||
|
var edittext3 = group3.add('edittext {properties: {name: "edittext3"}}');
|
||||||
|
edittext3.preferredSize.width = 100;
|
||||||
|
|
||||||
|
// GROUP4
|
||||||
|
// ======
|
||||||
|
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||||
|
group4.orientation = "row";
|
||||||
|
group4.alignChildren = ["left","center"];
|
||||||
|
group4.spacing = 10;
|
||||||
|
group4.margins = 0;
|
||||||
|
|
||||||
|
// GROUP5
|
||||||
|
// ======
|
||||||
|
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||||
|
group5.orientation = "row";
|
||||||
|
group5.alignChildren = ["left","center"];
|
||||||
|
group5.spacing = 10;
|
||||||
|
group5.margins = 0;
|
||||||
|
|
||||||
|
// PANEL3
|
||||||
|
// ======
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP7
|
||||||
|
// ======
|
||||||
|
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||||
|
group7.orientation = "column";
|
||||||
|
group7.alignChildren = ["fill","top"];
|
||||||
|
group7.spacing = 10;
|
||||||
|
group7.margins = 0;
|
||||||
|
|
||||||
|
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "确认";
|
||||||
|
|
||||||
|
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
button1.onClick = function () {
|
||||||
|
// 打开文件夹选择对话框
|
||||||
|
var selectedFolder = Folder.selectDialog("选择图像文件夹");
|
||||||
|
|
||||||
|
// 检查用户是否取消了选择
|
||||||
|
if (selectedFolder) {
|
||||||
|
// 将选择的文件夹路径显示在输入框中
|
||||||
|
edittext1.text = selectedFolder.fsName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ok.onClick = function () {
|
||||||
|
app.preferences.rulerUnits = Units.PIXELS;
|
||||||
|
var 素材图片文件夹 = new Folder(edittext1.text);
|
||||||
|
var 遍历tiff = 素材图片文件夹.getFiles("*.*");
|
||||||
|
|
||||||
|
// 新建文件夹
|
||||||
|
var 新文件夹 = new Folder(edittext1.text + "/SO小样拼贴");
|
||||||
|
新文件夹.create();
|
||||||
|
var 新加字文件夹 = new Folder(edittext1.text + "/SO小样拼贴加字");
|
||||||
|
新加字文件夹.create();
|
||||||
|
|
||||||
|
|
||||||
|
宽度=Number (edittext2.text);
|
||||||
|
高度=Number (edittext3.text);
|
||||||
|
|
||||||
|
|
||||||
|
for (var i = 0; i < 遍历tiff.length; i++) {
|
||||||
|
if (遍历tiff[i] instanceof File) {
|
||||||
|
app.open(遍历tiff[i]);
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
var 当前文档 = app.activeDocument;
|
||||||
|
var 当前文档名称 = 当前文档.name;
|
||||||
|
图像大小()
|
||||||
|
预设图案(当前文档名称);
|
||||||
|
app.activeDocument.crop([UnitValue("0px"), UnitValue("0px"), UnitValue(宽度, "cm"), UnitValue(高度, "cm")]);
|
||||||
|
填充图案(当前文档名称);
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
画布扩展()
|
||||||
|
// 保存新的图像文件到新建的文件夹中
|
||||||
|
var 保存路径 = 新文件夹 + "/" + 当前文档名称;
|
||||||
|
var tifSaveOpt = new TiffSaveOptions();
|
||||||
|
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||||
|
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||||
|
var asCopy = true;
|
||||||
|
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||||
|
创建并处理文本图层();
|
||||||
|
app.activeDocument.flatten();
|
||||||
|
var 保存路径 = 新加字文件夹 + "/" + 当前文档名称;
|
||||||
|
var tifSaveOpt = new TiffSaveOptions();
|
||||||
|
tifSaveOpt.imageCompression = TIFFEncoding.TIFFLZW;
|
||||||
|
tifSaveOpt.byteOrder = ByteOrder.IBM;
|
||||||
|
var asCopy = true;
|
||||||
|
app.activeDocument.saveAs(new File(保存路径), tifSaveOpt, asCopy);
|
||||||
|
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alert("小样连晒完成")
|
||||||
|
// 创建并保存拼贴图像(新文件夹, 幅宽)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 预设图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("pattern"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("property"), stringIDToTypeID("selection"));
|
||||||
|
r1.putEnumerated(stringIDToTypeID("document"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("using"), r1);
|
||||||
|
d.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 填充图案(当前文档名称) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putClass(stringIDToTypeID("contentLayer"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
var d2 = new ActionDescriptor();
|
||||||
|
var d3 = new ActionDescriptor();
|
||||||
|
d3.putString(stringIDToTypeID("name"), 当前文档名称);
|
||||||
|
|
||||||
|
d2.putObject(stringIDToTypeID("pattern"), stringIDToTypeID("pattern"), d3);
|
||||||
|
d1.putObject(stringIDToTypeID("type"), stringIDToTypeID("patternLayer"), d2);
|
||||||
|
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("contentLayer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 图像大小() //图像大小
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), 200);
|
||||||
|
d.putBoolean(stringIDToTypeID("scaleStyles"), true);
|
||||||
|
d.putBoolean(stringIDToTypeID("constrainProportions"), true);
|
||||||
|
d.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID("nearestNeighbor"));
|
||||||
|
executeAction(stringIDToTypeID("imageSize"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 画布扩展() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putBoolean(stringIDToTypeID("relative"), true);
|
||||||
|
d.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||||
|
d.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("distanceUnit"), 14.4);
|
||||||
|
d.putEnumerated(stringIDToTypeID("horizontal"), stringIDToTypeID("horizontalLocation"), stringIDToTypeID("center"));
|
||||||
|
d.putEnumerated(stringIDToTypeID("vertical"), stringIDToTypeID("verticalLocation"), stringIDToTypeID("center"));
|
||||||
|
d.putEnumerated(stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("canvasExtensionColorType"), stringIDToTypeID("color"));
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putDouble(stringIDToTypeID("red"), 255);
|
||||||
|
d1.putDouble(stringIDToTypeID("green"), 255);
|
||||||
|
d1.putDouble(stringIDToTypeID("blue"), 255);
|
||||||
|
d.putObject(stringIDToTypeID("canvasExtensionColor"), stringIDToTypeID("RGBColor"), d1);
|
||||||
|
executeAction(stringIDToTypeID("canvasSize"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 创建并处理文本图层() {
|
||||||
|
// 新建图层
|
||||||
|
var textLayer = activeDocument.artLayers.add();
|
||||||
|
|
||||||
|
// 将新建图层变成文本图层
|
||||||
|
textLayer.kind = LayerKind.TEXT;
|
||||||
|
|
||||||
|
// 将文本内容改为当前文档name
|
||||||
|
textLayer.textItem.contents = activeDocument.name;
|
||||||
|
|
||||||
|
// 字体大小固定值
|
||||||
|
var 固定字体大小 = 30; // 例如,30像素
|
||||||
|
textLayer.textItem.size = 固定字体大小;
|
||||||
|
|
||||||
|
// 文字字体固定值
|
||||||
|
textLayer.textItem.font = "微软雅黑";
|
||||||
|
|
||||||
|
// 计算并调整文本位置
|
||||||
|
var x = activeDocument.width - textLayer.bounds[2];
|
||||||
|
var y = textLayer.bounds[1];
|
||||||
|
textLayer.translate(x, -y);
|
||||||
|
|
||||||
|
// 偏移
|
||||||
|
textLayer.translate(UnitValue("-1cm"), UnitValue("+0.5cm"));
|
||||||
|
|
||||||
|
// 复制并栅格化图层
|
||||||
|
var copyLayer = textLayer.duplicate();
|
||||||
|
copyLayer.rasterize(RasterizeType.ENTIRELAYER);
|
||||||
|
|
||||||
|
// 设置固定颜色值
|
||||||
|
var 固定颜色 = new SolidColor();
|
||||||
|
固定颜色.rgb.red = 255; // 红色分量
|
||||||
|
固定颜色.rgb.green = 255; // 绿色分量
|
||||||
|
固定颜色.rgb.blue = 255; // 蓝色分量
|
||||||
|
|
||||||
|
activeDocument.activeLayer = copyLayer;
|
||||||
|
activeDocument.selection.fill(固定颜色, ColorBlendMode.NORMAL, 100, true);
|
||||||
|
|
||||||
|
// 白边
|
||||||
|
activeDocument.activeLayer.applyMinimum(10);
|
||||||
|
后移一层()
|
||||||
|
向上选择()
|
||||||
|
向下合并()
|
||||||
|
名称更改字体()
|
||||||
|
缩小字体图层至文档一半()
|
||||||
|
多选背景()
|
||||||
|
底对齐()
|
||||||
|
水平居中对齐()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用函数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 后移一层() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("previous"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("move"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 向上选择() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("forwardEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(19);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
function 向下合并() //向下合并
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 名称更改字体() //名称更改
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putString(stringIDToTypeID("name"), "字体");
|
||||||
|
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layer"), d1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 缩小字体图层至文档一半() {
|
||||||
|
var 文档 = app.activeDocument;
|
||||||
|
var 字体图层 = null;
|
||||||
|
|
||||||
|
// 遍历文档中的图层以找到名为"字体"的图层
|
||||||
|
for (var i = 0; i < 文档.artLayers.length; i++) {
|
||||||
|
if (文档.artLayers[i].name === "字体") {
|
||||||
|
字体图层 = 文档.artLayers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (字体图层 !== null) {
|
||||||
|
// 获取文档的宽度的一半
|
||||||
|
var 目标宽度 = 文档.width / 2;
|
||||||
|
|
||||||
|
// 获取图层的当前宽度
|
||||||
|
var 图层宽度 = 字体图层.bounds[2] - 字体图层.bounds[0];
|
||||||
|
|
||||||
|
// 计算缩放比例
|
||||||
|
var 缩放比例 = 目标宽度 / 图层宽度 * 100;
|
||||||
|
|
||||||
|
// 缩放图层
|
||||||
|
字体图层.resize(缩放比例, 缩放比例, AnchorPosition.MIDDLECENTER);
|
||||||
|
} else {
|
||||||
|
alert("未找到名为'字体'的图层");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 多选背景() //自由变换
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "背景");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("selectionModifier"), stringIDToTypeID("selectionModifierType"), stringIDToTypeID("addToSelection"));
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(1);
|
||||||
|
list.putInteger(13);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 底对齐() //底对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSBottoms"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 水平居中对齐() //水平居中对齐
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putEnumerated(stringIDToTypeID("using"), stringIDToTypeID("alignDistributeSelector"), stringIDToTypeID("ADSCentersH"));
|
||||||
|
d.putBoolean(stringIDToTypeID("alignToCanvas"), false);
|
||||||
|
executeAction(stringIDToTypeID("align"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 设置单位为像素
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
306
PSMARK代码块/码标添加2().jsx
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// 检查是否有打开的文档
|
||||||
|
|
||||||
|
function 码标添加2() {
|
||||||
|
|
||||||
|
app.activeDocument.suspendHistory("码标添加", "码标放置()");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 码标放置() {
|
||||||
|
app.activeDocument.layerSets.add().name = "码标";
|
||||||
|
|
||||||
|
app.preferences.rulerUnits = Units.PIXELS;
|
||||||
|
if (app.documents.length > 0) {
|
||||||
|
var doc = app.activeDocument; // 获取当前文档
|
||||||
|
|
||||||
|
// 查找名为"裁片"的组
|
||||||
|
var cropGroup = doc.layerSets.getByName("裁片"); // 将“裁片”替换为您的组名称
|
||||||
|
|
||||||
|
if (cropGroup) {
|
||||||
|
// 遍历裁片组内的所有图层
|
||||||
|
for (var i = 0; i < cropGroup.layers.length; i++) {
|
||||||
|
var layer = cropGroup.layers[i];
|
||||||
|
//alert("图层名称: " + layer.name);
|
||||||
|
当前图层名称=layer.name
|
||||||
|
parts = layer.name.split("_")
|
||||||
|
// alert(parts[2])
|
||||||
|
大货成品图层 = app.activeDocument.layerSets.getByName("裁片").layers.getByName(当前图层名称);
|
||||||
|
app.activeDocument.activeLayer = 大货成品图层;
|
||||||
|
切换mask();
|
||||||
|
|
||||||
|
载入选区蒙版()
|
||||||
|
大货成品图层边距 = 获取当前选区四边距();
|
||||||
|
右边距=大货成品图层边距.right
|
||||||
|
|
||||||
|
var currentDocument = app.activeDocument;
|
||||||
|
var height = currentDocument.height.value;
|
||||||
|
var 上边距新 = 0;
|
||||||
|
var 左边距新 = 右边距 - 1;
|
||||||
|
var 下边距新 = height;
|
||||||
|
var 右边距新 = 右边距 + 1;
|
||||||
|
新建选区(上边距新, 左边距新, 下边距新, 右边距新);
|
||||||
|
选取交叉()
|
||||||
|
获取码标记点 = 获取当前选区四边距();
|
||||||
|
码标记点下标记 = 获取码标记点.bottom
|
||||||
|
码标记点左标记 = 获取码标记点.left
|
||||||
|
码标x中心坐标=码标记点左标记
|
||||||
|
码标y中心坐标=码标记点下标记
|
||||||
|
$.writeln(码标x中心坐标);
|
||||||
|
$.writeln(码标y中心坐标);
|
||||||
|
载入选区蒙版()
|
||||||
|
收缩45像素()
|
||||||
|
收缩45像素成品图层边距 = 获取当前选区四边距();
|
||||||
|
收缩45像素右边距=收缩45像素成品图层边距.right
|
||||||
|
var 收缩45像素上边距新 = 0;
|
||||||
|
var 收缩45像素左边距新 = 0 ;
|
||||||
|
var 收缩45像素下边距新 = height;
|
||||||
|
var 收缩45像素右边距新 = 收缩45像素右边距-1 ;
|
||||||
|
//$.writeln(收缩45像素上边距新);
|
||||||
|
//$ .writeln(收缩45像素左边距新);
|
||||||
|
//$.writeln(收缩45像素下边距新);
|
||||||
|
//$.writeln(收缩45像素右边距新);
|
||||||
|
减去选区(收缩45像素上边距新, 收缩45像素左边距新, 收缩45像素下边距新, 收缩45像素右边距新);
|
||||||
|
//选取交叉()
|
||||||
|
收缩45像素获取码标记点 = 获取当前选区四边距();
|
||||||
|
收缩45像素码标记点y标记 = 收缩45像素获取码标记点.bottom
|
||||||
|
收缩45像素码标记点x标记 = 收缩45像素获取码标记点.left
|
||||||
|
$.writeln(收缩45像素码标记点x标记);
|
||||||
|
$.writeln(收缩45像素码标记点y标记);
|
||||||
|
|
||||||
|
获取中心点1=获取中心点(收缩45像素码标记点x标记, 收缩45像素码标记点y标记, 码标x中心坐标, 码标y中心坐标)
|
||||||
|
$.writeln("中心点坐标: x = " + 获取中心点1.x + ", y = " + 获取中心点1.y);
|
||||||
|
|
||||||
|
|
||||||
|
var 码标高度转毫米y = pixelsToMillimeters(获取中心点1.y);
|
||||||
|
var 码标宽度转毫米x = pixelsToMillimeters(获取中心点1.x );
|
||||||
|
|
||||||
|
//alert(码标高度转毫米)
|
||||||
|
//alert(码标宽度转毫米)
|
||||||
|
var fileName = currentDocument.name;
|
||||||
|
|
||||||
|
// 去掉文件名的后缀名
|
||||||
|
var fileNameWithoutExtension = fileName.split('.').slice(0, -1).join('.');
|
||||||
|
var textLayer = currentDocument.artLayers.add();
|
||||||
|
textLayer.kind = LayerKind.TEXT;
|
||||||
|
|
||||||
|
// 设置文本图层的文本内容
|
||||||
|
textLayer.textItem.contents = fileNameWithoutExtension
|
||||||
|
textLayer.textItem.size = 10
|
||||||
|
var cmykColor = new SolidColor();
|
||||||
|
cmykColor.cmyk.cyan = 50; // 青色通道值
|
||||||
|
cmykColor.cmyk.magenta = 40; // 品红色通道值
|
||||||
|
cmykColor.cmyk.yellow = 50; // 黄色通道值
|
||||||
|
cmykColor.cmyk.black = 70; // 黑色通道值
|
||||||
|
|
||||||
|
// 将文本图层的颜色设置为上面创建的CMYK颜色
|
||||||
|
textLayer.textItem.color = cmykColor;
|
||||||
|
|
||||||
|
app.preferences.rulerUnits = Units.MM;
|
||||||
|
|
||||||
|
当前图层 = app.activeDocument.activeLayer;
|
||||||
|
当前图层的底边 = 当前图层.bounds[3];
|
||||||
|
当前图层的上边 = 当前图层.bounds[1];
|
||||||
|
当前图层的高度 = 当前图层的底边 - 当前图层的上边;
|
||||||
|
当前图层的左边 = 当前图层.bounds[0];
|
||||||
|
当前图层的右边 = 当前图层.bounds[2];
|
||||||
|
当前图层的宽度 = 当前图层的右边 - 当前图层的左边;
|
||||||
|
当前图层的高度的一半 = 当前图层的高度 / 2;
|
||||||
|
当前图层的宽度的一半 = 当前图层的宽度 / 2;
|
||||||
|
当前图层的高度中心 = 当前图层的上边 + 当前图层的高度的一半;
|
||||||
|
当前图层的宽度中心 = 当前图层的左边 + 当前图层的宽度的一半;
|
||||||
|
|
||||||
|
|
||||||
|
//app.activeDocument.activeLayer.translate(Number(当前图层的宽度中心) - Number(码标宽度转毫米), Number(当前图层的高度中心) - Number(码标高度转毫米));
|
||||||
|
|
||||||
|
app.activeDocument.activeLayer.translate(Number(码标宽度转毫米x) - Number(当前图层的宽度中心), Number(码标高度转毫米y) - Number(当前图层的高度中心));
|
||||||
|
app.preferences.rulerUnits = Units.PIXELS;
|
||||||
|
app.activeDocument.activeLayer.move(app.activeDocument.layerSets.getByName("码标"), ElementPlacement.INSIDE);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("找不到名为“裁片”的组。");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("没有打开的文档。");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
码标 = app.activeDocument.layerSets.getByName("码标")
|
||||||
|
app.activeDocument.activeLayer = 码标;
|
||||||
|
描边()
|
||||||
|
|
||||||
|
function 减去选区(上边距, 左边距, 下边距, 右边距) //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putUnitDouble(stringIDToTypeID("top"), stringIDToTypeID("pixelsUnit"), 上边距);
|
||||||
|
d1.putUnitDouble(stringIDToTypeID("left"), stringIDToTypeID("pixelsUnit"), 左边距);
|
||||||
|
d1.putUnitDouble(stringIDToTypeID("bottom"), stringIDToTypeID("pixelsUnit"), 下边距);
|
||||||
|
d1.putUnitDouble(stringIDToTypeID("right"), stringIDToTypeID("pixelsUnit"), 右边距);
|
||||||
|
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("rectangle"), d1);
|
||||||
|
executeAction(stringIDToTypeID("subtractFrom"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 收缩45像素() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
d.putUnitDouble(stringIDToTypeID("by"), stringIDToTypeID("pixelsUnit"), 45);
|
||||||
|
d.putBoolean(stringIDToTypeID("selectionModifyEffectAtCanvasBounds"), false);
|
||||||
|
executeAction(stringIDToTypeID("contract"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 获取中心点(x1, y1, x2, y2) {
|
||||||
|
var centerX = (x1 + x2) / 2;
|
||||||
|
var centerY = (y1 + y2) / 2;
|
||||||
|
return { x: centerX, y: centerY };
|
||||||
|
}
|
||||||
|
|
||||||
|
function 切换mask() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("channel"), stringIDToTypeID("mask"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 选择组() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putName(stringIDToTypeID("layer"), "码标");
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
d.putBoolean(stringIDToTypeID("makeVisible"), false);
|
||||||
|
var list = new ActionList();
|
||||||
|
list.putInteger(153);
|
||||||
|
d.putList(stringIDToTypeID("layerID"), list);
|
||||||
|
executeAction(stringIDToTypeID("select"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
function 合并组() //合并组
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
executeAction(stringIDToTypeID("mergeLayersNew"), d, DialogModes.NO);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 新建选区(上边距, 左边距, 下边距, 右边距) {
|
||||||
|
var currentDocument = app.activeDocument;
|
||||||
|
var top = 上边距;
|
||||||
|
var left = 左边距;
|
||||||
|
var bottom = 下边距;
|
||||||
|
var right = 右边距;
|
||||||
|
|
||||||
|
var selectionRegion = Array(Array(left, top), Array(right, top), Array(right, bottom), Array(left, bottom));
|
||||||
|
currentDocument.selection.select(selectionRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 载入选区蒙版() //载入选区
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("to"), r1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
function 获取当前选区四边距() {
|
||||||
|
var currentDocument = app.activeDocument;
|
||||||
|
var selectionBounds = currentDocument.selection.bounds;
|
||||||
|
|
||||||
|
var top = selectionBounds[1].value;
|
||||||
|
var left = selectionBounds[0].value;
|
||||||
|
var bottom = selectionBounds[3].value;
|
||||||
|
var right = selectionBounds[2].value;
|
||||||
|
|
||||||
|
return {
|
||||||
|
top: top,
|
||||||
|
left: left,
|
||||||
|
bottom: bottom,
|
||||||
|
right: right
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function 描边() //描边
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putProperty(stringIDToTypeID("property"), stringIDToTypeID("layerEffects"));
|
||||||
|
r.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var d1 = new ActionDescriptor();
|
||||||
|
d1.putUnitDouble(stringIDToTypeID("scale"), stringIDToTypeID("percentUnit"), 208.333290947808);
|
||||||
|
var d2 = new ActionDescriptor();
|
||||||
|
d2.putBoolean(stringIDToTypeID("enabled"), true);
|
||||||
|
d2.putBoolean(stringIDToTypeID("present"), true);
|
||||||
|
d2.putBoolean(stringIDToTypeID("showInDialog"), true);
|
||||||
|
d2.putEnumerated(stringIDToTypeID("style"), stringIDToTypeID("frameStyle"), stringIDToTypeID("outsetFrame"));
|
||||||
|
d2.putEnumerated(stringIDToTypeID("paintType"), stringIDToTypeID("frameFill"), stringIDToTypeID("solidColor"));
|
||||||
|
d2.putEnumerated(stringIDToTypeID("mode"), stringIDToTypeID("blendMode"), stringIDToTypeID("normal"));
|
||||||
|
d2.putUnitDouble(stringIDToTypeID("opacity"), stringIDToTypeID("percentUnit"), 100);
|
||||||
|
d2.putUnitDouble(stringIDToTypeID("size"), stringIDToTypeID("pixelsUnit"), 2);
|
||||||
|
var d3 = new ActionDescriptor();
|
||||||
|
d3.putDouble(stringIDToTypeID("cyan"), 0);
|
||||||
|
d3.putDouble(stringIDToTypeID("magenta"), 0);
|
||||||
|
d3.putDouble(stringIDToTypeID("yellowColor"), 0);
|
||||||
|
d3.putDouble(stringIDToTypeID("black"), 0);
|
||||||
|
d2.putObject(stringIDToTypeID("color"), stringIDToTypeID("CMYKColorClass"), d3);
|
||||||
|
d2.putBoolean(stringIDToTypeID("overprint"), false);
|
||||||
|
d1.putObject(stringIDToTypeID("frameFX"), stringIDToTypeID("frameFX"), d2);
|
||||||
|
d.putObject(stringIDToTypeID("to"), stringIDToTypeID("layerEffects"), d1);
|
||||||
|
executeAction(stringIDToTypeID("set"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将像素转换为毫米
|
||||||
|
function pixelsToMillimeters(pixels) {
|
||||||
|
// 获取当前文档
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
|
||||||
|
// 获取图像的分辨率(像素/英寸)
|
||||||
|
var resolution = doc.resolution;
|
||||||
|
|
||||||
|
// 计算像素转换为毫米
|
||||||
|
var inches = pixels / resolution;
|
||||||
|
var millimeters = inches * 25.4;
|
||||||
|
|
||||||
|
return millimeters.toFixed(2); // 保留两位小数
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function 选取交叉() //
|
||||||
|
{
|
||||||
|
|
||||||
|
var d = new ActionDescriptor();
|
||||||
|
var r = new ActionReference();
|
||||||
|
r.putEnumerated(stringIDToTypeID("channel"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
d.putReference(stringIDToTypeID("null"), r);
|
||||||
|
var r1 = new ActionReference();
|
||||||
|
r1.putProperty(stringIDToTypeID("channel"), stringIDToTypeID("selection"));
|
||||||
|
d.putReference(stringIDToTypeID("with"), r1);
|
||||||
|
executeAction(charIDToTypeID("Intr"), d, DialogModes.NO);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
dxf24_jscode = """
|
|
||||||
|
自动米样拼贴()
|
||||||
|
|
||||||
|
|
||||||
// 设置单位为像素
|
// 设置单位为像素
|
||||||
@@ -254,7 +254,7 @@ ok.onClick = function () {
|
|||||||
// 假设这些值是从某处获取或者用户输入的
|
// 假设这些值是从某处获取或者用户输入的
|
||||||
var 导入文件夹路径 =new Folder (edittext1.text);
|
var 导入文件夹路径 =new Folder (edittext1.text);
|
||||||
var 文档宽度厘米 = Number(edittext2.text);
|
var 文档宽度厘米 = Number(edittext2.text);
|
||||||
var 文档高度厘米 = 300; // 例如,50厘米
|
var 文档高度厘米 = 600; // 例如,50厘米
|
||||||
var 分辨率 = 200; // 分辨率设置为150 DPI
|
var 分辨率 = 200; // 分辨率设置为150 DPI
|
||||||
|
|
||||||
// 转换厘米到像素
|
// 转换厘米到像素
|
||||||
@@ -365,6 +365,3 @@ alert("拼贴完成")
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
dxf27_jscode = """
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
新的米样缩放()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -497,4 +495,3 @@ dialog.show();
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
361
PSMARK代码块/米样自动拼贴.jsx
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
// 设置单位为像素
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
|
||||||
|
|
||||||
|
dialog.text = "SO自动米样拼贴";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "源图像";
|
||||||
|
panel1.preferredSize.width = 388;
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "使用:";
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "row";
|
||||||
|
group2.alignChildren = ["left","center"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "选取";
|
||||||
|
|
||||||
|
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||||
|
edittext1.text = "[选择图像文件夹]";
|
||||||
|
edittext1.preferredSize.width = 300;
|
||||||
|
|
||||||
|
// PANEL2
|
||||||
|
// ======
|
||||||
|
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||||
|
panel2.text = "SO小样文档";
|
||||||
|
panel2.preferredSize.height = 160;
|
||||||
|
panel2.orientation = "column";
|
||||||
|
panel2.alignChildren = ["left","top"];
|
||||||
|
panel2.spacing = 10;
|
||||||
|
panel2.margins = 10;
|
||||||
|
|
||||||
|
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "设定:";
|
||||||
|
|
||||||
|
// GROUP3
|
||||||
|
// ======
|
||||||
|
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||||
|
group3.orientation = "row";
|
||||||
|
group3.alignChildren = ["left","center"];
|
||||||
|
group3.spacing = 10;
|
||||||
|
group3.margins = 0;
|
||||||
|
|
||||||
|
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "幅宽(cm):";
|
||||||
|
|
||||||
|
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||||
|
edittext2.preferredSize.width = 100;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP4
|
||||||
|
// ======
|
||||||
|
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||||
|
group4.orientation = "row";
|
||||||
|
group4.alignChildren = ["left","center"];
|
||||||
|
group4.spacing = 10;
|
||||||
|
group4.margins = 0;
|
||||||
|
|
||||||
|
// GROUP5
|
||||||
|
// ======
|
||||||
|
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||||
|
group5.orientation = "row";
|
||||||
|
group5.alignChildren = ["left","center"];
|
||||||
|
group5.spacing = 10;
|
||||||
|
group5.margins = 0;
|
||||||
|
|
||||||
|
// PANEL3
|
||||||
|
// ======
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP7
|
||||||
|
// ======
|
||||||
|
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||||
|
group7.orientation = "column";
|
||||||
|
group7.alignChildren = ["fill","top"];
|
||||||
|
group7.spacing = 10;
|
||||||
|
group7.margins = 0;
|
||||||
|
|
||||||
|
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "确认";
|
||||||
|
|
||||||
|
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var dialog = new Window("dialog");
|
||||||
|
dialog.text = "SO自动米样拼贴";
|
||||||
|
dialog.orientation = "row";
|
||||||
|
dialog.alignChildren = ["left","top"];
|
||||||
|
dialog.spacing = 10;
|
||||||
|
dialog.margins = 16;
|
||||||
|
|
||||||
|
// GROUP1
|
||||||
|
// ======
|
||||||
|
var group1 = dialog.add("group", undefined, {name: "group1"});
|
||||||
|
group1.orientation = "column";
|
||||||
|
group1.alignChildren = ["fill","top"];
|
||||||
|
group1.spacing = 10;
|
||||||
|
group1.margins = 0;
|
||||||
|
|
||||||
|
// PANEL1
|
||||||
|
// ======
|
||||||
|
var panel1 = group1.add("panel", undefined, undefined, {name: "panel1"});
|
||||||
|
panel1.text = "源图像";
|
||||||
|
panel1.preferredSize.width = 388;
|
||||||
|
panel1.preferredSize.height = 205;
|
||||||
|
panel1.orientation = "column";
|
||||||
|
panel1.alignChildren = ["left","top"];
|
||||||
|
panel1.spacing = 10;
|
||||||
|
panel1.margins = 10;
|
||||||
|
|
||||||
|
var statictext1 = panel1.add("statictext", undefined, undefined, {name: "statictext1"});
|
||||||
|
statictext1.text = "使用:";
|
||||||
|
|
||||||
|
// GROUP2
|
||||||
|
// ======
|
||||||
|
var group2 = panel1.add("group", undefined, {name: "group2"});
|
||||||
|
group2.orientation = "row";
|
||||||
|
group2.alignChildren = ["left","center"];
|
||||||
|
group2.spacing = 10;
|
||||||
|
group2.margins = 0;
|
||||||
|
|
||||||
|
var button1 = group2.add("button", undefined, undefined, {name: "button1"});
|
||||||
|
button1.text = "选取";
|
||||||
|
|
||||||
|
var edittext1 = group2.add('edittext {properties: {name: "edittext1"}}');
|
||||||
|
edittext1.text = "[选择图像文件夹]";
|
||||||
|
edittext1.preferredSize.width = 300;
|
||||||
|
|
||||||
|
// PANEL2
|
||||||
|
// ======
|
||||||
|
var panel2 = group1.add("panel", undefined, undefined, {name: "panel2"});
|
||||||
|
panel2.text = "SO小样文档拼贴";
|
||||||
|
panel2.preferredSize.height = 160;
|
||||||
|
panel2.orientation = "column";
|
||||||
|
panel2.alignChildren = ["left","top"];
|
||||||
|
panel2.spacing = 10;
|
||||||
|
panel2.margins = 10;
|
||||||
|
|
||||||
|
var statictext2 = panel2.add("statictext", undefined, undefined, {name: "statictext2"});
|
||||||
|
statictext2.text = "设定:";
|
||||||
|
|
||||||
|
// GROUP3
|
||||||
|
// ======
|
||||||
|
var group3 = panel2.add("group", undefined, {name: "group3"});
|
||||||
|
group3.orientation = "row";
|
||||||
|
group3.alignChildren = ["left","center"];
|
||||||
|
group3.spacing = 10;
|
||||||
|
group3.margins = 0;
|
||||||
|
|
||||||
|
var statictext3 = group3.add("statictext", undefined, undefined, {name: "statictext3"});
|
||||||
|
statictext3.text = "幅宽(cm):";
|
||||||
|
|
||||||
|
var edittext2 = group3.add('edittext {properties: {name: "edittext2"}}');
|
||||||
|
edittext2.preferredSize.width = 100;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP4
|
||||||
|
// ======
|
||||||
|
var group4 = panel2.add("group", undefined, {name: "group4"});
|
||||||
|
group4.orientation = "row";
|
||||||
|
group4.alignChildren = ["left","center"];
|
||||||
|
group4.spacing = 10;
|
||||||
|
group4.margins = 0;
|
||||||
|
|
||||||
|
// GROUP5
|
||||||
|
// ======
|
||||||
|
var group5 = group1.add("group", undefined, {name: "group5"});
|
||||||
|
group5.orientation = "row";
|
||||||
|
group5.alignChildren = ["left","center"];
|
||||||
|
group5.spacing = 10;
|
||||||
|
group5.margins = 0;
|
||||||
|
|
||||||
|
// PANEL3
|
||||||
|
// ======
|
||||||
|
|
||||||
|
|
||||||
|
// GROUP7
|
||||||
|
// ======
|
||||||
|
var group7 = dialog.add("group", undefined, {name: "group7"});
|
||||||
|
group7.orientation = "column";
|
||||||
|
group7.alignChildren = ["fill","top"];
|
||||||
|
group7.spacing = 10;
|
||||||
|
group7.margins = 0;
|
||||||
|
|
||||||
|
var ok = group7.add("button", undefined, undefined, {name: "ok"});
|
||||||
|
ok.text = "确认";
|
||||||
|
|
||||||
|
var cancel = group7.add("button", undefined, undefined, {name: "cancel"});
|
||||||
|
cancel.text = "取消";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
button1.onClick = function () {
|
||||||
|
// 打开文件夹选择对话框
|
||||||
|
var selectedFolder = Folder.selectDialog("选择图像文件夹");
|
||||||
|
|
||||||
|
// 检查用户是否取消了选择
|
||||||
|
if (selectedFolder) {
|
||||||
|
// 将选择的文件夹路径显示在输入框中
|
||||||
|
edittext1.text = selectedFolder.fsName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ok.onClick = function () {
|
||||||
|
app.preferences.rulerUnits = Units.PIXELS;
|
||||||
|
//function 创建拼贴图(导入文件夹路径, 文档宽度厘米) {
|
||||||
|
|
||||||
|
// 假设这些值是从某处获取或者用户输入的
|
||||||
|
var 导入文件夹路径 =new Folder (edittext1.text);
|
||||||
|
var 文档宽度厘米 = Number(edittext2.text);
|
||||||
|
var 文档高度厘米 = 300; // 例如,50厘米
|
||||||
|
var 分辨率 = 150; // 分辨率设置为150 DPI
|
||||||
|
|
||||||
|
// 转换厘米到像素
|
||||||
|
var 厘米到像素的转换系数 = 2.54;
|
||||||
|
var 文档宽度像素 = 文档宽度厘米 / 厘米到像素的转换系数 * 分辨率;
|
||||||
|
var 文档高度像素 = 文档高度厘米 / 厘米到像素的转换系数 * 分辨率;
|
||||||
|
|
||||||
|
var 导入文件夹 = new Folder(导入文件夹路径);
|
||||||
|
var 文件列表 = 导入文件夹.getFiles("*.*");
|
||||||
|
var 文件名数组 = new Array();
|
||||||
|
var 文件宽度数组 = new Array();
|
||||||
|
var 文件高度数组 = new Array();
|
||||||
|
|
||||||
|
|
||||||
|
// 读取文件列表并获取文件信息
|
||||||
|
for (var 索引1 = 0; 索引1 < 文件列表.length; 索引1++) {
|
||||||
|
var 文档 = app.open(文件列表[索引1]);
|
||||||
|
文件名数组[索引1] = 文档.fullName;
|
||||||
|
文件宽度数组[索引1] = parseInt(文档.width);
|
||||||
|
文件高度数组[索引1] = parseInt(文档.height);
|
||||||
|
文档.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据高度对文件进行排序
|
||||||
|
for (var 索引1 = 文件高度数组.length - 1; 索引1 > 0; --索引1) {
|
||||||
|
for (var 索引2 = 0; 索引2 < 索引1; ++索引2) {
|
||||||
|
if (文件高度数组[索引2] > 文件高度数组[索引2 + 1])
|
||||||
|
交换数组元素(索引2, 索引2 + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function 交换数组元素(索引1, 索引2) {
|
||||||
|
var 临时 = 文件高度数组[索引1];
|
||||||
|
文件高度数组[索引1] = 文件高度数组[索引2];
|
||||||
|
文件高度数组[索引2] = 临时;
|
||||||
|
|
||||||
|
var 临时2 = 文件宽度数组[索引1];
|
||||||
|
文件宽度数组[索引1] = 文件宽度数组[索引2];
|
||||||
|
文件宽度数组[索引2] = 临时2;
|
||||||
|
|
||||||
|
var 临时3 = 文件名数组[索引1];
|
||||||
|
文件名数组[索引1] = 文件名数组[索引2];
|
||||||
|
文件名数组[索引2] = 临时3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新文档并添加图片
|
||||||
|
var 新文档 = app.documents.add(文档宽度像素, 文档高度像素, 分辨率, "拼贴", NewDocumentMode.CMYK, DocumentFill.WHITE);
|
||||||
|
var 当前顶部 = 0;
|
||||||
|
var 当前左侧 = 0;
|
||||||
|
for (var 索引1 = 0; 索引1 < 文件名数组.length; 索引1++) {
|
||||||
|
var 文档 = app.open(文件名数组[索引1]);
|
||||||
|
文档.selection.selectAll();
|
||||||
|
文档.selection.copy();
|
||||||
|
文档.close();
|
||||||
|
|
||||||
|
if ((当前左侧 + 文件宽度数组[索引1]) > 文档宽度像素) {
|
||||||
|
当前左侧 = 0;
|
||||||
|
当前顶部 += 文件高度数组[索引1 - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
var 边界 = [
|
||||||
|
[当前左侧, 当前顶部],
|
||||||
|
[当前左侧 + 文件宽度数组[索引1], 当前顶部],
|
||||||
|
[当前左侧 + 文件宽度数组[索引1], 当前顶部 + 文件高度数组[索引1]],
|
||||||
|
[当前左侧, 当前顶部 + 文件高度数组[索引1]]
|
||||||
|
];
|
||||||
|
新文档.selection.select(边界, SelectionType.REPLACE, 0, true);
|
||||||
|
新文档.paste();
|
||||||
|
当前左侧 += 文件宽度数组[索引1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择输出文件夹并保存图片
|
||||||
|
var 输出文件夹 = new Folder(edittext1.text + "/拼贴");
|
||||||
|
if (!输出文件夹.exists) 输出文件夹.create();
|
||||||
|
|
||||||
|
// 设置TIFF保存选项
|
||||||
|
var tiffSaveOptions = new TiffSaveOptions();
|
||||||
|
tiffSaveOptions.imageCompression = TIFFEncoding.TIFFLZW; // 使用LZW压缩
|
||||||
|
tiffSaveOptions.byteOrder = ByteOrder.IBM; // 设置字节顺序为IBM(大端序)
|
||||||
|
|
||||||
|
// 定义保存的文件路径和名称
|
||||||
|
var 文件 = new File(输出文件夹 + "/combined.tif");
|
||||||
|
|
||||||
|
// 保存当前文档为TIFF格式
|
||||||
|
新文档.saveAs(文件, tiffSaveOptions, true, Extension.LOWERCASE);
|
||||||
|
裁切透明像素();
|
||||||
|
|
||||||
|
// 关闭文档,不保存更改
|
||||||
|
//新文档.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
dialog.close();
|
||||||
|
alert("拼贴完成")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function 裁切透明像素() {
|
||||||
|
var 文档 = app.activeDocument; // 获取当前活动的文档
|
||||||
|
|
||||||
|
// 裁切透明像素
|
||||||
|
// TrimType.TOPLEFT 表示从图像的左上角开始裁切
|
||||||
|
// 第二个参数 true 表示裁切顶部的透明像素
|
||||||
|
// 第三个参数 true 表示裁切左侧的透明像素
|
||||||
|
// 第四个参数 true 表示裁切底部的透明像素
|
||||||
|
// 第五个参数 true 表示裁切右侧的透明像素
|
||||||
|
文档.trim(TrimType.TOPLEFT, true, true, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
dxf23_jscode = """
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
自动连晒()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -471,4 +471,3 @@ dialog.show();
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
49
PSMARK代码块/自由变换函数.jsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function 自由变换(水平位置, 垂直位置, 水平偏移, 垂直偏移, 宽度百分比, 高度百分比, 保持宽高比, 插值方法,角度) {
|
||||||
|
try {
|
||||||
|
var 描述符 = new ActionDescriptor();
|
||||||
|
var 引用 = new ActionReference();
|
||||||
|
|
||||||
|
// 引用当前选中的图层
|
||||||
|
引用.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
|
||||||
|
描述符.putReference(stringIDToTypeID("null"), 引用);
|
||||||
|
|
||||||
|
// 设置自由变换的中心状态为独立
|
||||||
|
描述符.putEnumerated(stringIDToTypeID("freeTransformCenterState"), stringIDToTypeID("quadCenterState"), stringIDToTypeID("QCSIndependent"));
|
||||||
|
|
||||||
|
// 设置位置
|
||||||
|
var 位置描述符 = new ActionDescriptor();
|
||||||
|
位置描述符.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 水平位置);
|
||||||
|
位置描述符.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 垂直位置);
|
||||||
|
描述符.putObject(stringIDToTypeID("position"), stringIDToTypeID("point"), 位置描述符);
|
||||||
|
|
||||||
|
// 设置偏移
|
||||||
|
var 偏移描述符 = new ActionDescriptor();
|
||||||
|
偏移描述符.putUnitDouble(stringIDToTypeID("horizontal"), stringIDToTypeID("pixelsUnit"), 水平偏移);
|
||||||
|
偏移描述符.putUnitDouble(stringIDToTypeID("vertical"), stringIDToTypeID("pixelsUnit"), 垂直偏移);
|
||||||
|
描述符.putObject(stringIDToTypeID("offset"), stringIDToTypeID("offset"), 偏移描述符);
|
||||||
|
|
||||||
|
// 设置宽度和高度
|
||||||
|
描述符.putUnitDouble(stringIDToTypeID("width"), stringIDToTypeID("percentUnit"), 宽度百分比);
|
||||||
|
描述符.putUnitDouble(stringIDToTypeID("height"), stringIDToTypeID("percentUnit"), 高度百分比);
|
||||||
|
|
||||||
|
// 设置是否保持宽高比
|
||||||
|
描述符.putBoolean(stringIDToTypeID("linked"), 保持宽高比);
|
||||||
|
|
||||||
|
// 设置插值方法
|
||||||
|
描述符.putEnumerated(charIDToTypeID("Intr"), stringIDToTypeID("interpolationType"), stringIDToTypeID(插值方法));
|
||||||
|
描述符.putUnitDouble(stringIDToTypeID("angle"), stringIDToTypeID("angleUnit"), 角度);
|
||||||
|
// 执行变换
|
||||||
|
executeAction(stringIDToTypeID("transform"), 描述符, DialogModes.NO);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.number != 8007) {
|
||||||
|
alert("行号: " + e.line + e, "错误", true);
|
||||||
|
throw(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 调用示例
|
||||||
|
自由变换(水平位置, 垂直位置, 水平偏移, 垂直偏移, 宽度百分比, 高度百分比, true, 插值方法,角度);
|
||||||
28
PSMARK代码块/读取json文件.jsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// 创建一个文件对象指向桌面上的子图层名称.json文件
|
||||||
|
var desktop = Folder.desktop;
|
||||||
|
var file = new File(desktop + "/名称数据.json");
|
||||||
|
|
||||||
|
// 打开文件以进行读取
|
||||||
|
if (file.open('r')) {
|
||||||
|
var content = file.read();
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// 从JSON格式的字符串中删除前后的方括号,并根据逗号分割为数组
|
||||||
|
var 名称数组 = content.replace(/^\s*\[|\]\s*$/g, '').split(/"\s*,\s*"/);
|
||||||
|
|
||||||
|
// 循环遍历数组并逐个弹出名称
|
||||||
|
for (var i = 0; i < 名称数组.length; i++) {
|
||||||
|
// 从每个名称中删除前后的双引号
|
||||||
|
var 名称 = 名称数组[i].replace(/^"|"$/g, '');
|
||||||
|
|
||||||
|
// 弹出整个名称
|
||||||
|
// alert("完整名称: " + 名称);
|
||||||
|
|
||||||
|
// 使用正则表达式提取括号内的内容
|
||||||
|
var matches = 名称.match(/\(([^)]+)\)/);
|
||||||
|
var 图案名称=matches [1]
|
||||||
|
//alert("图案名称: " + 图案名称);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("无法打开文件!");
|
||||||
|
}
|
||||||
50
PSMARK代码块/读取图层json.jsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// 检查是否有活动文档
|
||||||
|
if (app.documents.length > 0) {
|
||||||
|
var doc = app.activeDocument; // 获取活动文档
|
||||||
|
|
||||||
|
// 递归函数来构建所有图层的字符串表示
|
||||||
|
function buildLayersString(layerSet, indent) {
|
||||||
|
var result = "";
|
||||||
|
var currentIndent = Array(indent + 1).join(" "); // 根据缩进级别构建缩进字符串
|
||||||
|
for (var i = 0; i < layerSet.layers.length; i++) {
|
||||||
|
var layer = layerSet.layers[i];
|
||||||
|
var layerName = layer.name.replace(/"/g, '\\"'); // 转义引号
|
||||||
|
|
||||||
|
if (layer.typename === 'ArtLayer') {
|
||||||
|
// 构建图层的字符串表示
|
||||||
|
result += '\n' + currentIndent + '\"图层: ' + layerName + '\": null';
|
||||||
|
} else { // LayerSet
|
||||||
|
// 构建图层组的字符串表示,并递归处理子图层
|
||||||
|
var childResult = buildLayersString(layer, indent + 1);
|
||||||
|
if (childResult !== "") {
|
||||||
|
result += '\n' + currentIndent + '\"图层组: ' + layerName + '\": {' + childResult + '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < layerSet.layers.length - 1) {
|
||||||
|
result += ','; // 如果不是最后一个图层或图层组,添加逗号
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var layersString = "{" + buildLayersString(doc, 0) + "\n}";
|
||||||
|
|
||||||
|
// 删除最后一个逗号
|
||||||
|
layersString = layersString.replace(/,\s*$/, '');
|
||||||
|
|
||||||
|
// 创建文件并写入数据
|
||||||
|
var desktopPath = Folder.desktop.fsName; // 获取桌面路径
|
||||||
|
var filePath = desktopPath + "/photoshop_layers.json"; // 文件完整路径
|
||||||
|
var file = new File(filePath);
|
||||||
|
|
||||||
|
// 打开文件进行写入,并使用GBK编码
|
||||||
|
file.open("w");
|
||||||
|
file.encoding = "GBK";
|
||||||
|
file.write(layersString);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
alert("文件已保存至桌面: photoshop_layers.json");
|
||||||
|
} else {
|
||||||
|
alert("没有打开的文档");
|
||||||
|
}
|
||||||
44
PSMARK代码块/遍历图层.jsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// 确保Photoshop中有打开的文档
|
||||||
|
if (app.documents.length == 0) {
|
||||||
|
$.writeln("没有打开的文档!");
|
||||||
|
} else {
|
||||||
|
// 获取当前激活的文档
|
||||||
|
var doc = app.activeDocument;
|
||||||
|
|
||||||
|
// 指定要遍历的图层组名称
|
||||||
|
var targetLayerSetName = "组 1"; // 请替换为您的图层组名称
|
||||||
|
|
||||||
|
// 查找并遍历指定的图层组
|
||||||
|
var targetLayerSet = findLayerSet(doc, targetLayerSetName);
|
||||||
|
if (targetLayerSet != null) {
|
||||||
|
traverseLayers(targetLayerSet, "");
|
||||||
|
} else {
|
||||||
|
$.writeln("未找到名为 '" + targetLayerSetName + "' 的图层组!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 函数:查找指定名称的图层组
|
||||||
|
function findLayerSet(doc, name) {
|
||||||
|
for (var i = 0; i < doc.layerSets.length; i++) {
|
||||||
|
if (doc.layerSets[i].name == name) {
|
||||||
|
return doc.layerSets[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 函数:遍历图层
|
||||||
|
function traverseLayers(layerSet, indent) {
|
||||||
|
// 遍历图层组中的所有图层
|
||||||
|
for (var i = 0; i < layerSet.layers.length; i++) {
|
||||||
|
var layer = layerSet.layers[i];
|
||||||
|
|
||||||
|
// 在控制台打印图层名称及其在图层组中的位置
|
||||||
|
$.writeln(indent + layer.name);
|
||||||
|
|
||||||
|
// 如果是图层组,递归遍历该组内的图层
|
||||||
|
if (layer.typename == "LayerSet") {
|
||||||
|
traverseLayers(layer, indent + " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
PltService/Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# PLT 处理微服务 Dockerfile
|
||||||
|
# 用于部署到阿里云 SAE
|
||||||
|
|
||||||
|
FROM python:3.10-slim
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 安装系统依赖(OpenCV 需要)
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libgl1-mesa-glx \
|
||||||
|
libglib2.0-0 \
|
||||||
|
libsm6 \
|
||||||
|
libxext6 \
|
||||||
|
libxrender-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 复制依赖文件
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# 安装 Python 依赖
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
|
||||||
|
|
||||||
|
# 复制代码
|
||||||
|
COPY pltreader.py .
|
||||||
|
COPY main.py .
|
||||||
|
|
||||||
|
# 暴露端口(SAE 默认使用 8080)
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# 启动命令
|
||||||
|
CMD ["python", "main.py"]
|
||||||
112
PltService/README.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# PLT 裁片处理微服务
|
||||||
|
|
||||||
|
独立的 PLT 文件处理服务,可部署到阿里云 SAE。
|
||||||
|
|
||||||
|
## 本地运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装依赖
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# 启动服务
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
服务启动后访问:http://localhost:8080
|
||||||
|
|
||||||
|
## Docker 构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 构建镜像
|
||||||
|
docker build -t plt-service:latest .
|
||||||
|
|
||||||
|
# 运行容器
|
||||||
|
docker run -p 8080:8080 plt-service:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## 部署到阿里云 SAE
|
||||||
|
|
||||||
|
### 1. 构建并推送镜像到阿里云容器镜像服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 登录阿里云容器镜像服务
|
||||||
|
docker login --username=<你的阿里云账号> registry.cn-hangzhou.aliyuncs.com
|
||||||
|
|
||||||
|
# 构建镜像
|
||||||
|
docker build -t registry.cn-hangzhou.aliyuncs.com/<命名空间>/plt-service:v1.0 .
|
||||||
|
|
||||||
|
# 推送镜像
|
||||||
|
docker push registry.cn-hangzhou.aliyuncs.com/<命名空间>/plt-service:v1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 在 SAE 创建应用
|
||||||
|
|
||||||
|
1. 进入阿里云 SAE 控制台
|
||||||
|
2. 创建应用 → 选择"镜像部署"
|
||||||
|
3. 填写镜像地址:`registry.cn-hangzhou.aliyuncs.com/<命名空间>/plt-service:v1.0`
|
||||||
|
4. 配置规格:
|
||||||
|
- CPU: 2核
|
||||||
|
- 内存: 4GB
|
||||||
|
- 最小实例数: 0(无请求时不收费)
|
||||||
|
- 最大实例数: 5
|
||||||
|
5. 完成创建
|
||||||
|
|
||||||
|
### 3. 配置公网访问
|
||||||
|
|
||||||
|
在 SAE 应用详情 → 基本信息 → SLB 设置 → 添加公网 SLB
|
||||||
|
|
||||||
|
## API 接口
|
||||||
|
|
||||||
|
### 健康检查
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /health
|
||||||
|
```
|
||||||
|
|
||||||
|
### 处理 PLT 文件
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /process
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- file: PLT 文件
|
||||||
|
- size_labels: 尺码标签,如 ["S","M","L","XL","2XL"]
|
||||||
|
- dpi: 输出分辨率(默认 150)
|
||||||
|
- rotation: 旋转角度(0/90/-90/180)
|
||||||
|
```
|
||||||
|
|
||||||
|
响应示例:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"total_groups": 5,
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"group_id": 1,
|
||||||
|
"pieces": [
|
||||||
|
{
|
||||||
|
"size": "S",
|
||||||
|
"image_base64": "data:image/png;base64,...",
|
||||||
|
"width_px": 500,
|
||||||
|
"height_px": 300,
|
||||||
|
"width_cm": 25.5,
|
||||||
|
"height_cm": 15.3,
|
||||||
|
"center_x_cm": 12.75,
|
||||||
|
"center_y_cm": 7.65,
|
||||||
|
"left_cm": 0,
|
||||||
|
"top_cm": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 费用估算(阿里云 SAE)
|
||||||
|
|
||||||
|
| 场景 | 费用 |
|
||||||
|
|------|------|
|
||||||
|
| 处理 1 个 PLT(30秒) | ¥0.04 |
|
||||||
|
| 每天 100 个 PLT | ¥4/天 |
|
||||||
|
| 无请求时 | ¥0(最小实例设为0) |
|
||||||
261
PltService/main.py
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
PLT 裁片处理微服务
|
||||||
|
独立部署到阿里云 SAE
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import io
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
from typing import List, Optional
|
||||||
|
from fastapi import FastAPI, UploadFile, File, Form, HTTPException
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
from PIL import Image
|
||||||
|
from shapely import affinity
|
||||||
|
from scipy.optimize import linear_sum_assignment
|
||||||
|
|
||||||
|
from pltreader import PltReader
|
||||||
|
|
||||||
|
app = FastAPI(title="PLT Processing Service")
|
||||||
|
|
||||||
|
# CORS 配置
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# ==================== 数据模型 ====================
|
||||||
|
|
||||||
|
class PieceInfo(BaseModel):
|
||||||
|
"""单个裁片信息"""
|
||||||
|
size: str
|
||||||
|
image_base64: str
|
||||||
|
width_px: int
|
||||||
|
height_px: int
|
||||||
|
width_cm: float
|
||||||
|
height_cm: float
|
||||||
|
center_x_cm: float
|
||||||
|
center_y_cm: float
|
||||||
|
left_cm: float
|
||||||
|
top_cm: float
|
||||||
|
|
||||||
|
class GroupInfo(BaseModel):
|
||||||
|
"""裁片分组信息"""
|
||||||
|
group_id: int
|
||||||
|
pieces: List[PieceInfo]
|
||||||
|
|
||||||
|
class ProcessPltResponse(BaseModel):
|
||||||
|
"""API 响应"""
|
||||||
|
success: bool
|
||||||
|
total_groups: int
|
||||||
|
groups: List[GroupInfo]
|
||||||
|
|
||||||
|
# ==================== 辅助函数 ====================
|
||||||
|
|
||||||
|
def parse_plt_file(file_content: str, tolerance: int = 10):
|
||||||
|
"""解析 PLT 文件内容"""
|
||||||
|
reader = PltReader(io.StringIO(file_content))
|
||||||
|
output = reader.get_output(tolerance=tolerance)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def apply_rotation_to_image(pil_img: Image.Image, rotation: int) -> Image.Image:
|
||||||
|
"""应用旋转变换"""
|
||||||
|
if rotation == 90:
|
||||||
|
return pil_img.rotate(-90, expand=True)
|
||||||
|
elif rotation == -90:
|
||||||
|
return pil_img.rotate(90, expand=True)
|
||||||
|
elif rotation == 180:
|
||||||
|
return pil_img.rotate(180, expand=True)
|
||||||
|
return pil_img
|
||||||
|
|
||||||
|
def calculate_piece_coordinates(polygon, bounds, plt_to_cm, rotation=0):
|
||||||
|
"""计算裁片坐标(厘米单位)"""
|
||||||
|
min_x, min_y, max_x, max_y = bounds
|
||||||
|
|
||||||
|
centroid = polygon.centroid
|
||||||
|
orig_center_x = (centroid.x - min_x) * plt_to_cm
|
||||||
|
orig_center_y = (centroid.y - min_y) * plt_to_cm
|
||||||
|
|
||||||
|
piece_bounds = polygon.bounds
|
||||||
|
orig_piece_width = (piece_bounds[2] - piece_bounds[0]) * plt_to_cm
|
||||||
|
orig_piece_height = (piece_bounds[3] - piece_bounds[1]) * plt_to_cm
|
||||||
|
|
||||||
|
orig_canvas_width = (max_x - min_x) * plt_to_cm
|
||||||
|
orig_canvas_height = (max_y - min_y) * plt_to_cm
|
||||||
|
|
||||||
|
if rotation == 90:
|
||||||
|
center_x = orig_center_y
|
||||||
|
center_y = orig_canvas_width - orig_center_x
|
||||||
|
piece_width, piece_height = orig_piece_height, orig_piece_width
|
||||||
|
elif rotation == -90:
|
||||||
|
center_x = orig_canvas_height - orig_center_y
|
||||||
|
center_y = orig_center_x
|
||||||
|
piece_width, piece_height = orig_piece_height, orig_piece_width
|
||||||
|
elif rotation == 180:
|
||||||
|
center_x = orig_canvas_width - orig_center_x
|
||||||
|
center_y = orig_canvas_height - orig_center_y
|
||||||
|
piece_width, piece_height = orig_piece_width, orig_piece_height
|
||||||
|
else:
|
||||||
|
center_x, center_y = orig_center_x, orig_center_y
|
||||||
|
piece_width, piece_height = orig_piece_width, orig_piece_height
|
||||||
|
|
||||||
|
left_cm = center_x - piece_width / 2
|
||||||
|
top_cm = center_y - piece_height / 2
|
||||||
|
|
||||||
|
return {
|
||||||
|
"center_x_cm": round(center_x, 2),
|
||||||
|
"center_y_cm": round(center_y, 2),
|
||||||
|
"left_cm": round(left_cm, 2),
|
||||||
|
"top_cm": round(top_cm, 2),
|
||||||
|
"width_cm": round(piece_width, 2),
|
||||||
|
"height_cm": round(piece_height, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==================== API 接口 ====================
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
async def health_check():
|
||||||
|
"""健康检查"""
|
||||||
|
return {"status": "healthy", "service": "plt-processor"}
|
||||||
|
|
||||||
|
@app.post("/process", response_model=ProcessPltResponse)
|
||||||
|
async def process_plt(
|
||||||
|
file: UploadFile = File(..., description="PLT 文件"),
|
||||||
|
size_labels: str = Form(..., description="尺码标签 JSON 数组"),
|
||||||
|
dpi: int = Form(150, description="输出图片分辨率(DPI)"),
|
||||||
|
rotation: int = Form(0, description="强制旋转角度 (0/90/-90/180)")
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
PLT 裁片处理接口
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 1. 解析参数
|
||||||
|
try:
|
||||||
|
size_labels_list = json.loads(size_labels)
|
||||||
|
except:
|
||||||
|
raise HTTPException(status_code=400, detail="size_labels 格式错误")
|
||||||
|
|
||||||
|
size_num = len(size_labels_list)
|
||||||
|
scale_factor = dpi / 1016
|
||||||
|
plt_to_cm = 2.54 / 1016
|
||||||
|
|
||||||
|
# 2. 读取文件
|
||||||
|
file_content = (await file.read()).decode('utf-8', errors='ignore')
|
||||||
|
|
||||||
|
# 3. 解析 PLT
|
||||||
|
print(f"[PLT] 正在处理文件: {file.filename}")
|
||||||
|
output = parse_plt_file(file_content)
|
||||||
|
|
||||||
|
# 4. 获取尺码聚类
|
||||||
|
clusters = output.get_single_size_info(size_num=size_num)
|
||||||
|
|
||||||
|
# 5. 建立匹配关系
|
||||||
|
base_cluster = [piece for piece in clusters[0] if piece["parent"] is None]
|
||||||
|
match_result = {piece["index"]: [piece["index"]] for piece in base_cluster}
|
||||||
|
|
||||||
|
for size_index in range(1, len(clusters)):
|
||||||
|
compare_cluster = [piece for piece in clusters[size_index] if piece["parent"] is None]
|
||||||
|
|
||||||
|
cost_matrix = np.zeros((len(base_cluster), len(compare_cluster)))
|
||||||
|
|
||||||
|
for i in range(len(base_cluster)):
|
||||||
|
for j in range(len(compare_cluster)):
|
||||||
|
poly1 = base_cluster[i]["data"]
|
||||||
|
poly2 = compare_cluster[j]["data"]
|
||||||
|
|
||||||
|
poly1 = affinity.translate(poly1, xoff=-poly1.centroid.x, yoff=-poly1.centroid.y)
|
||||||
|
poly2 = affinity.translate(poly2, xoff=-poly2.centroid.x, yoff=-poly2.centroid.y)
|
||||||
|
|
||||||
|
target_area = 10000
|
||||||
|
scale1 = (target_area / poly1.area) ** 0.5
|
||||||
|
scale2 = (target_area / poly2.area) ** 0.5
|
||||||
|
poly1 = affinity.scale(poly1, xfact=scale1, yfact=scale1, origin=(0, 0))
|
||||||
|
poly2 = affinity.scale(poly2, xfact=scale2, yfact=scale2, origin=(0, 0))
|
||||||
|
|
||||||
|
dist_min = float('inf')
|
||||||
|
for angle in [0, 90, 180, 270]:
|
||||||
|
rotated = affinity.rotate(poly2, angle, origin='centroid')
|
||||||
|
dist = poly1.hausdorff_distance(rotated)
|
||||||
|
if dist < dist_min:
|
||||||
|
dist_min = dist
|
||||||
|
|
||||||
|
cost_matrix[i, j] = dist_min
|
||||||
|
|
||||||
|
row_ind, col_ind = linear_sum_assignment(cost_matrix)
|
||||||
|
for i, j in zip(row_ind, col_ind):
|
||||||
|
base_index = base_cluster[i]['index']
|
||||||
|
matched_index = compare_cluster[j]['index']
|
||||||
|
match_result[base_index].append(matched_index)
|
||||||
|
|
||||||
|
# 6. 生成图片
|
||||||
|
all_nodes = {node["index"]: node for node in output.nodes}
|
||||||
|
|
||||||
|
from shapely.geometry import MultiPolygon as ShapelyMultiPolygon
|
||||||
|
all_polygons = [node["data"] for node in output.nodes]
|
||||||
|
global_bounds = ShapelyMultiPolygon(all_polygons).bounds
|
||||||
|
|
||||||
|
groups = []
|
||||||
|
|
||||||
|
for group_id, (base_index, matched_indices) in enumerate(match_result.items(), start=1):
|
||||||
|
pieces = []
|
||||||
|
|
||||||
|
for size_idx, piece_index in enumerate(matched_indices):
|
||||||
|
size_label = size_labels_list[size_idx]
|
||||||
|
|
||||||
|
node = all_nodes[piece_index]
|
||||||
|
nodes_to_draw = [node] + node['child']
|
||||||
|
|
||||||
|
img = output._draw_nodes(nodes_to_draw, scale_factor, show_id=False)
|
||||||
|
|
||||||
|
img_rgba = cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA)
|
||||||
|
pil_img = Image.fromarray(img_rgba)
|
||||||
|
pil_img = apply_rotation_to_image(pil_img, rotation)
|
||||||
|
|
||||||
|
buffer = io.BytesIO()
|
||||||
|
pil_img.save(buffer, format='PNG', dpi=(dpi, dpi))
|
||||||
|
buffer.seek(0)
|
||||||
|
base64_str = base64.b64encode(buffer.read()).decode('utf-8')
|
||||||
|
image_base64 = f"data:image/png;base64,{base64_str}"
|
||||||
|
|
||||||
|
coords = calculate_piece_coordinates(
|
||||||
|
node["data"],
|
||||||
|
global_bounds,
|
||||||
|
plt_to_cm,
|
||||||
|
rotation
|
||||||
|
)
|
||||||
|
|
||||||
|
piece_info = PieceInfo(
|
||||||
|
size=size_label,
|
||||||
|
image_base64=image_base64,
|
||||||
|
width_px=pil_img.width,
|
||||||
|
height_px=pil_img.height,
|
||||||
|
**coords
|
||||||
|
)
|
||||||
|
pieces.append(piece_info)
|
||||||
|
|
||||||
|
groups.append(GroupInfo(group_id=group_id, pieces=pieces))
|
||||||
|
|
||||||
|
print(f"[PLT] 处理完成,共 {len(groups)} 组裁片")
|
||||||
|
return ProcessPltResponse(
|
||||||
|
success=True,
|
||||||
|
total_groups=len(groups),
|
||||||
|
groups=groups
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[PLT] 处理失败: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
raise HTTPException(status_code=500, detail=f"处理失败: {str(e)}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
port = int(os.getenv("PORT", 8080))
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=port)
|
||||||
620
PltService/pltreader.py
Normal file
@@ -0,0 +1,620 @@
|
|||||||
|
'''
|
||||||
|
参考资料:
|
||||||
|
https://www.gnu.org/software/hp2xx/hp2xx.html
|
||||||
|
https://zhuanlan.zhihu.com/p/622090369
|
||||||
|
'''
|
||||||
|
|
||||||
|
import os
|
||||||
|
import typing
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
import warnings
|
||||||
|
from enum import Enum
|
||||||
|
import shapely
|
||||||
|
import shapely.ops
|
||||||
|
from shapely.geometry import LineString, Polygon, MultiPolygon, MultiLineString
|
||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
import itertools
|
||||||
|
import logging
|
||||||
|
from shapely.validation import make_valid
|
||||||
|
|
||||||
|
SHOW_PLT_WARNINGS = False
|
||||||
|
|
||||||
|
# 如果没配置日志则配置日志
|
||||||
|
if not logging.getLogger().handlers:
|
||||||
|
logging.basicConfig(level=logging.INFO, format='[%(asctime)s][%(name)s][%(levelname)s] %(message)s')
|
||||||
|
|
||||||
|
|
||||||
|
def assert_warning(condition, message):
|
||||||
|
if SHOW_PLT_WARNINGS and (not condition):
|
||||||
|
logging.warning(message)
|
||||||
|
|
||||||
|
|
||||||
|
class PltCommand(Enum):
|
||||||
|
IN = 0
|
||||||
|
PU = 1
|
||||||
|
PD = 2
|
||||||
|
PA = 3
|
||||||
|
|
||||||
|
AbstractSyntaxTree = typing.List[typing.Dict]
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualMachineOutput(object):
|
||||||
|
'''虚拟机执行后的结果'''
|
||||||
|
MIN_FILTER_AREA_FACTOR = 0.0068 ** 2
|
||||||
|
MAX_FILTER_AREA_FACTOR = 0.8 ** 2
|
||||||
|
|
||||||
|
def __init__(self, polygons :typing.List[Polygon], dispensable: typing.List[LineString]) -> None:
|
||||||
|
for poly in polygons:
|
||||||
|
assert isinstance(poly, Polygon)
|
||||||
|
for line in dispensable:
|
||||||
|
assert isinstance(line, LineString)
|
||||||
|
|
||||||
|
# 过滤结果
|
||||||
|
|
||||||
|
# 计算画布大小
|
||||||
|
min_x, min_y, max_x, max_y = MultiPolygon(polygons).bounds
|
||||||
|
|
||||||
|
filter_polygons = []
|
||||||
|
for polygon in polygons:
|
||||||
|
if (max_x - min_x) * (max_y - min_y) * VirtualMachineOutput.MIN_FILTER_AREA_FACTOR < polygon.area < (max_x - min_x) * (max_y - min_y) * VirtualMachineOutput.MAX_FILTER_AREA_FACTOR:
|
||||||
|
filter_polygons.append(polygon)
|
||||||
|
else:
|
||||||
|
boundary = polygon.boundary
|
||||||
|
if isinstance(boundary, LineString):
|
||||||
|
dispensable.append(boundary)
|
||||||
|
elif isinstance(boundary, MultiLineString):
|
||||||
|
dispensable.extend(list(boundary.geoms))
|
||||||
|
|
||||||
|
logging.info(f"过滤了{len(polygons) - len(filter_polygons)}个多边形")
|
||||||
|
polygons = filter_polygons
|
||||||
|
|
||||||
|
# 计算多边形的包含关系,构建多边形树
|
||||||
|
self.nodes = [{"data":polygon, "child":[], "parent":None, "index":index} for index, polygon in enumerate(polygons)]
|
||||||
|
self.dispensable = dispensable
|
||||||
|
|
||||||
|
# 找每个node的最佳parent
|
||||||
|
for node1 in self.nodes:
|
||||||
|
best_parent = None
|
||||||
|
for node2 in self.nodes:
|
||||||
|
if node1 == node2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if node1["index"] == 8 and node2["index"] == 9:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 判断是否包含
|
||||||
|
#if node2["data"].contains(node1["data"]):
|
||||||
|
if node2["data"].area > node1["data"].area:
|
||||||
|
intersection = node2["data"].intersection(node1["data"])
|
||||||
|
if intersection.area > node1["data"].area * 0.99:
|
||||||
|
if best_parent is None:
|
||||||
|
best_parent = node2
|
||||||
|
else:
|
||||||
|
if best_parent["data"].contains(node2["data"]):
|
||||||
|
best_parent = node2
|
||||||
|
|
||||||
|
node1["parent"] = best_parent
|
||||||
|
|
||||||
|
# 填充child
|
||||||
|
self.tree = []
|
||||||
|
for node in self.nodes:
|
||||||
|
if node["parent"] is not None:
|
||||||
|
node["parent"]["child"].append(node)
|
||||||
|
else:
|
||||||
|
self.tree.append(node)
|
||||||
|
|
||||||
|
# 构建广度优先遍历
|
||||||
|
self.bfs = self.tree.copy()
|
||||||
|
index = 0
|
||||||
|
while index < len(self.bfs):
|
||||||
|
self.bfs.extend(self.bfs[index]["child"])
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
def _draw_nodes(self, nodes: typing.List, scale_factor: float, show_id: bool = True) -> np.ndarray:
|
||||||
|
# 计算画布大小
|
||||||
|
min_x, min_y, max_x, max_y = MultiPolygon([node["data"] for node in nodes]).bounds
|
||||||
|
|
||||||
|
max_x = int(np.ceil(max_x * scale_factor))
|
||||||
|
max_y = int(np.ceil(max_y * scale_factor))
|
||||||
|
min_x = int(np.floor(min_x * scale_factor))
|
||||||
|
min_y = int(np.floor(min_y * scale_factor))
|
||||||
|
|
||||||
|
max_x -= min_x
|
||||||
|
max_y -= min_y
|
||||||
|
|
||||||
|
#print("画布大小", max_x, max_y)
|
||||||
|
|
||||||
|
result_img = np.zeros((max_y, max_x, 4), dtype=np.uint8)
|
||||||
|
|
||||||
|
nodes = sorted(nodes, key=lambda node: self.bfs.index(node))
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
pts = (np.asarray(node["data"].exterior.coords) * scale_factor - [min_x, min_y]).astype(np.int32)
|
||||||
|
pts[:,1] = max_y - pts[:,1]
|
||||||
|
cv2.fillPoly(result_img, [pts], (255,255,255,255))
|
||||||
|
cv2.polylines(result_img, [pts], True, (0,0,0,255), 2)
|
||||||
|
|
||||||
|
# 画无关紧要的东西
|
||||||
|
multiPolygon = MultiPolygon([node["data"] for node in nodes])
|
||||||
|
multiPolygon = make_valid(multiPolygon.buffer(0))
|
||||||
|
for shape in self.dispensable:
|
||||||
|
if multiPolygon.contains(shape):
|
||||||
|
pts = (np.asarray(shape.coords) * scale_factor - [min_x, min_y]).astype(np.int32)
|
||||||
|
pts[:,1] = max_y - pts[:,1]
|
||||||
|
cv2.polylines(result_img, [pts], False, (0,0,0,255), 1)
|
||||||
|
|
||||||
|
# 画parent为None的id
|
||||||
|
if show_id:
|
||||||
|
for node in nodes:
|
||||||
|
if node["parent"] is None:
|
||||||
|
centroid = node["data"].centroid
|
||||||
|
text_pos = (int((centroid.x * scale_factor) - min_x), int(max_y - (centroid.y * scale_factor) - min_y))
|
||||||
|
cv2.putText(result_img, str(node["index"]), text_pos, cv2.FONT_HERSHEY_SIMPLEX, 3, (255,0,0,255), 5)
|
||||||
|
|
||||||
|
return result_img
|
||||||
|
|
||||||
|
def full_sheet(self, scale_factor: float = 0.1) -> np.ndarray:
|
||||||
|
'''整幅输出'''
|
||||||
|
return self._draw_nodes(self.bfs, scale_factor)
|
||||||
|
|
||||||
|
def debug_full_sheet(self, scale_factor: float = 0.1) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
用随机颜色画出所有多边形和被过滤掉的线段
|
||||||
|
"""
|
||||||
|
# 计算画布大小,画布大小应该考虑dispensable
|
||||||
|
all_shapes = [node["data"] for node in self.nodes] + self.dispensable
|
||||||
|
temp = []
|
||||||
|
for shape in all_shapes:
|
||||||
|
if isinstance(shape, LineString):
|
||||||
|
convex_hull = shape.convex_hull
|
||||||
|
if isinstance(convex_hull, Polygon):
|
||||||
|
temp.append(convex_hull)
|
||||||
|
else:
|
||||||
|
temp.append(shape)
|
||||||
|
min_x, min_y, max_x, max_y = MultiPolygon(temp).bounds
|
||||||
|
max_x = int(np.ceil(max_x * scale_factor))
|
||||||
|
max_y = int(np.ceil(max_y * scale_factor))
|
||||||
|
min_x = int(np.floor(min_x * scale_factor))
|
||||||
|
min_y = int(np.floor(min_y * scale_factor))
|
||||||
|
max_x -= min_x
|
||||||
|
max_y -= min_y
|
||||||
|
|
||||||
|
result_img = np.zeros((max_y, max_x, 4), dtype=np.uint8)
|
||||||
|
|
||||||
|
# 画多边形
|
||||||
|
for node in self.nodes:
|
||||||
|
#overlay = result_img.copy()
|
||||||
|
color = tuple(np.random.randint(0,256,size=3).tolist() + [255])
|
||||||
|
pts = (np.asarray(node["data"].exterior.coords) * scale_factor - [min_x, min_y]).astype(np.int32)
|
||||||
|
pts[:,1] = max_y - pts[:,1]
|
||||||
|
cv2.fillPoly(result_img, [pts], color)
|
||||||
|
if node["parent"] is None:
|
||||||
|
cv2.polylines(result_img, [pts], True, (0,0,255,255), 1)
|
||||||
|
else:
|
||||||
|
cv2.polylines(result_img, [pts], True, (0,0,0,255), 1)
|
||||||
|
#alpha = 0.5
|
||||||
|
#cv2.addWeighted(overlay, alpha, result_img, 1 - alpha, 0, result_img)
|
||||||
|
|
||||||
|
# 画被过滤掉的线段
|
||||||
|
for shape in self.dispensable:
|
||||||
|
#overlay = result_img.copy()
|
||||||
|
color = tuple(np.random.randint(0,256,size=3).tolist() + [255])
|
||||||
|
pts = (np.asarray(shape.coords) * scale_factor - [min_x, min_y]).astype(np.int32)
|
||||||
|
pts[:,1] = max_y - pts[:,1]
|
||||||
|
cv2.polylines(result_img, [pts], False, color, 1)
|
||||||
|
#cv2.addWeighted(overlay, alpha, result_img, 1 - alpha, 0, result_img)
|
||||||
|
|
||||||
|
# 画parent为None的id
|
||||||
|
for node in self.nodes:
|
||||||
|
if node["parent"] is None:
|
||||||
|
centroid = node["data"].centroid
|
||||||
|
text_pos = (int((centroid.x * scale_factor) - min_x), int(max_y - (centroid.y * scale_factor) - min_y))
|
||||||
|
cv2.putText(result_img, str(node["index"]), text_pos, cv2.FONT_HERSHEY_SIMPLEX, 3, (0,0,255,255), 5)
|
||||||
|
|
||||||
|
|
||||||
|
return result_img
|
||||||
|
|
||||||
|
def _distance_between_cluster(self, cluster1, cluster2):
|
||||||
|
min_distance = float("inf")
|
||||||
|
for node1 in cluster1:
|
||||||
|
for node2 in cluster2:
|
||||||
|
assert isinstance(node1["data"], Polygon)
|
||||||
|
assert isinstance(node2["data"], Polygon)
|
||||||
|
|
||||||
|
d = node1["data"].distance(node2["data"])
|
||||||
|
if d < min_distance:
|
||||||
|
min_distance = d
|
||||||
|
return min_distance
|
||||||
|
|
||||||
|
def get_single_size_info(self, size_num: int) -> typing.List[typing.List]:
|
||||||
|
'''获取单码信息'''
|
||||||
|
logging.info(f"开始进行距离聚类,目标聚类数目: {size_num}")
|
||||||
|
|
||||||
|
# 计算所有多边形两两之间的距离
|
||||||
|
distances: list[tuple[float, int, int]] = []
|
||||||
|
for i in range(len(self.nodes)):
|
||||||
|
for j in range(i + 1, len(self.nodes)):
|
||||||
|
poly1 = self.nodes[i]["data"]
|
||||||
|
poly2 = self.nodes[j]["data"]
|
||||||
|
dist = poly1.distance(poly2)
|
||||||
|
distances.append((dist, i, j))
|
||||||
|
|
||||||
|
distances.sort(key=lambda x: x[0])
|
||||||
|
|
||||||
|
# 使用并查集进行聚类
|
||||||
|
parent = list(range(len(self.nodes)))
|
||||||
|
def find(i):
|
||||||
|
if parent[i] != i:
|
||||||
|
parent[i] = find(parent[i]) # 路径压缩
|
||||||
|
return parent[i]
|
||||||
|
def union(i, j):
|
||||||
|
root_i = find(i)
|
||||||
|
root_j = find(j)
|
||||||
|
parent[root_i] = root_j
|
||||||
|
return root_i != root_j
|
||||||
|
|
||||||
|
# 进行聚类直到聚类数目达到size_num
|
||||||
|
current_cluster_count = len(self.nodes)
|
||||||
|
for _, i, j in distances:
|
||||||
|
if current_cluster_count <= size_num:
|
||||||
|
break
|
||||||
|
if union(i, j):
|
||||||
|
current_cluster_count -= 1
|
||||||
|
|
||||||
|
# 构建聚类结果
|
||||||
|
clusters_dict = {}
|
||||||
|
for i in range(len(self.nodes)):
|
||||||
|
root = find(i)
|
||||||
|
if root not in clusters_dict:
|
||||||
|
clusters_dict[root] = []
|
||||||
|
clusters_dict[root].append(self.nodes[i])
|
||||||
|
clusters = list(clusters_dict.values())
|
||||||
|
logging.info(f"距离聚类得到{len(clusters)}个聚类")
|
||||||
|
|
||||||
|
# 排序
|
||||||
|
sorted_clusters = sorted(clusters, key=lambda x: min([node["index"] for node in x]))
|
||||||
|
|
||||||
|
return list(sorted_clusters)
|
||||||
|
|
||||||
|
def single_size(self, size_num: int, scale_factor: float = 0.1) -> typing.List[np.ndarray]:
|
||||||
|
'''单码输出'''
|
||||||
|
sorted_clusters = self.get_single_size_info(size_num)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for cluster in sorted_clusters:
|
||||||
|
result.append(self._draw_nodes(cluster, scale_factor))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def single_piece(self, scale_factor: float = 0.1) -> typing.List[np.ndarray]:
|
||||||
|
'''单片输出'''
|
||||||
|
result = []
|
||||||
|
for node in self.nodes:
|
||||||
|
# 如果是顶层则新建一副图像
|
||||||
|
if node["parent"] is None:
|
||||||
|
nodes = [node] + node['child']
|
||||||
|
result.append(self._draw_nodes(nodes, scale_factor))
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class _VirtualMachine(object):
|
||||||
|
'''运行PLT文件的虚拟机'''
|
||||||
|
def __init__(self, tolerance) -> None:
|
||||||
|
self.tolerance = tolerance
|
||||||
|
|
||||||
|
def __run_v2(self, abstract_syntax_tree: AbstractSyntaxTree) -> VirtualMachineOutput:
|
||||||
|
current_x, current_y = 0, 0
|
||||||
|
pen_state = 'UP'
|
||||||
|
raw_segments: typing.List[typing.List[typing.Tuple[float, float]]] = []
|
||||||
|
current_path: typing.List[typing.Tuple[float, float]] = []
|
||||||
|
|
||||||
|
for code in abstract_syntax_tree:
|
||||||
|
try:
|
||||||
|
if code["cmd"] == PltCommand.IN:
|
||||||
|
logging.info("初始化")
|
||||||
|
|
||||||
|
elif code["cmd"] == PltCommand.PA:
|
||||||
|
new_x = float(code["args"][0])
|
||||||
|
new_y = float(code["args"][1])
|
||||||
|
|
||||||
|
if pen_state == 'DOWN':
|
||||||
|
if not current_path:
|
||||||
|
current_path.append((current_x, current_y))
|
||||||
|
current_path.append((new_x, new_y))
|
||||||
|
elif pen_state == 'UP':
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 更新当前位置
|
||||||
|
current_x, current_y = new_x, new_y
|
||||||
|
|
||||||
|
elif code["cmd"] == PltCommand.PU:
|
||||||
|
pen_state = 'UP'
|
||||||
|
if current_path:
|
||||||
|
assert len(current_path) > 1 and len(set(current_path)) > 1
|
||||||
|
raw_segments.append(current_path)
|
||||||
|
current_path = []
|
||||||
|
|
||||||
|
elif code["cmd"] == PltCommand.PD:
|
||||||
|
pen_state = 'DOWN'
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
assert_warning(False, f"[code {code}] 执行指令异常: {e}")
|
||||||
|
|
||||||
|
# 处理最后一条路径
|
||||||
|
if current_path:
|
||||||
|
assert len(current_path) > 1 and len(set(current_path)) > 1
|
||||||
|
raw_segments.append(current_path)
|
||||||
|
current_path = []
|
||||||
|
|
||||||
|
merged_geometry = shapely.ops.linemerge(raw_segments)
|
||||||
|
|
||||||
|
final_lines: typing.List[LineString] = []
|
||||||
|
|
||||||
|
if isinstance(merged_geometry, LineString):
|
||||||
|
final_lines.append(merged_geometry)
|
||||||
|
elif isinstance(merged_geometry, MultiLineString):
|
||||||
|
final_lines.extend(merged_geometry.geoms)
|
||||||
|
|
||||||
|
return self.__build_polygons_and_dispensable(final_lines)
|
||||||
|
|
||||||
|
def run(self, abstract_syntax_tree: AbstractSyntaxTree, version: int = 2) -> VirtualMachineOutput:
|
||||||
|
if version == 1:
|
||||||
|
logging.warning("版本1已过时 请使用版本2")
|
||||||
|
return self.__run_v1(abstract_syntax_tree)
|
||||||
|
elif version == 2:
|
||||||
|
return self.__run_v2(abstract_syntax_tree)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的版本号: {version}")
|
||||||
|
|
||||||
|
def __run_v1(self, abstract_syntax_tree: AbstractSyntaxTree) -> VirtualMachineOutput:
|
||||||
|
context = abstract_syntax_tree.copy()
|
||||||
|
current_x = 0
|
||||||
|
current_y = 0
|
||||||
|
self.pen_state = 'UP'
|
||||||
|
lines :typing.List[typing.List[typing.Tuple]] = []
|
||||||
|
lines_flag :typing.List[int] = []
|
||||||
|
|
||||||
|
while len(context) != 0:
|
||||||
|
code = context[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
if code["cmd"] == PltCommand.IN:
|
||||||
|
logging.info("初始化")
|
||||||
|
|
||||||
|
elif code["cmd"] == PltCommand.PA:
|
||||||
|
if self.pen_state == 'UP':
|
||||||
|
pass
|
||||||
|
elif self.pen_state == 'DOWN':
|
||||||
|
new_x = float(code["args"][0])
|
||||||
|
new_y = float(code["args"][1])
|
||||||
|
|
||||||
|
# 判断是否与已有的线相连接
|
||||||
|
for i in range(len(lines) - 1, -1, -1):
|
||||||
|
if lines_flag[i] == 1:
|
||||||
|
continue
|
||||||
|
if lines[i][0] == (current_x, current_y):
|
||||||
|
lines[i].insert(0, (new_x, new_y))
|
||||||
|
if lines[i][0] == lines[i][-1]:
|
||||||
|
lines_flag[i] = 1
|
||||||
|
break
|
||||||
|
elif lines[i][0] == (new_x, new_y):
|
||||||
|
lines[i].insert(0, (current_x, current_y))
|
||||||
|
if lines[i][0] == lines[i][-1]:
|
||||||
|
lines_flag[i] = 1
|
||||||
|
break
|
||||||
|
elif lines[i][-1] == (current_x, current_y):
|
||||||
|
lines[i].append((new_x, new_y))
|
||||||
|
if lines[i][0] == lines[i][-1]:
|
||||||
|
lines_flag[i] = 1
|
||||||
|
break
|
||||||
|
elif lines[i][-1] == (new_x, new_y):
|
||||||
|
lines[i].append((current_x, current_y))
|
||||||
|
if lines[i][0] == lines[i][-1]:
|
||||||
|
lines_flag[i] = 1
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# 不与已有的线相连接
|
||||||
|
lines.append([(current_x, current_y), (new_x, new_y)])
|
||||||
|
lines_flag.append(0)
|
||||||
|
|
||||||
|
current_x = float(code["args"][0])
|
||||||
|
current_y = float(code["args"][1])
|
||||||
|
|
||||||
|
elif code["cmd"] == PltCommand.PU:
|
||||||
|
self.pen_state = 'UP'
|
||||||
|
|
||||||
|
elif code["cmd"] == PltCommand.PD:
|
||||||
|
self.pen_state = 'DOWN'
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
assert_warning(False, f"[code {code}] 执行指令异常: {e}")
|
||||||
|
finally:
|
||||||
|
context = context[1:]
|
||||||
|
|
||||||
|
# 创建LineString :typing.List[LineString] = []
|
||||||
|
lineStrings :typing.List[LineString] = []
|
||||||
|
lineStrings = [LineString(line) for line in lines if len(set(line)) > 1]
|
||||||
|
|
||||||
|
'''
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
for line in lineStrings:
|
||||||
|
x, y = line.xy
|
||||||
|
plt.plot(x, y)
|
||||||
|
plt.show()
|
||||||
|
'''
|
||||||
|
|
||||||
|
return self.__build_polygons_and_dispensable(lineStrings)
|
||||||
|
|
||||||
|
def __build_polygons_and_dispensable(self, lineStrings: typing.List[LineString]) -> VirtualMachineOutput:
|
||||||
|
|
||||||
|
# 为非常近的线搭桥
|
||||||
|
tree = shapely.STRtree(lineStrings)
|
||||||
|
|
||||||
|
bridges = []
|
||||||
|
# 遍历所有线段的端点,寻找需要搭桥的地方
|
||||||
|
for line in lineStrings:
|
||||||
|
endpoints = [shapely.Point(line.coords[0]), shapely.Point(line.coords[-1])]
|
||||||
|
|
||||||
|
for p in endpoints:
|
||||||
|
for idx in tree.query(p.buffer(self.tolerance)):
|
||||||
|
other_line = lineStrings[idx]
|
||||||
|
if other_line == line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
dist = p.distance(other_line)
|
||||||
|
|
||||||
|
if 0 < dist <= self.tolerance:
|
||||||
|
_, p_target = shapely.ops.nearest_points(p, other_line)
|
||||||
|
bridge = LineString([p, p_target])
|
||||||
|
bridges.append(bridge)
|
||||||
|
|
||||||
|
lineStrings.extend(bridges)
|
||||||
|
|
||||||
|
# 节点化并提取闭合区域
|
||||||
|
all_shapes = shapely.ops.unary_union(lineStrings)
|
||||||
|
assert isinstance(all_shapes, MultiLineString)
|
||||||
|
polygons :typing.List[Polygon] = list(shapely.ops.polygonize(all_shapes))
|
||||||
|
|
||||||
|
# 合并重叠多边形
|
||||||
|
merged_polygons = shapely.unary_union(polygons)
|
||||||
|
assert isinstance(merged_polygons, MultiPolygon)
|
||||||
|
polygons = list(merged_polygons.geoms)
|
||||||
|
|
||||||
|
# 只保留外环
|
||||||
|
polygons = [Polygon(poly.exterior) for poly in polygons]
|
||||||
|
|
||||||
|
# 找到多余的线段
|
||||||
|
dispensable :typing.List[LineString] = []
|
||||||
|
closed_rings = [poly.exterior for poly in polygons]
|
||||||
|
if closed_rings:
|
||||||
|
leftovers = all_shapes.difference(shapely.ops.unary_union(closed_rings))
|
||||||
|
assert isinstance(leftovers, shapely.MultiLineString)
|
||||||
|
for shape in leftovers.geoms:
|
||||||
|
dispensable.append(shape)
|
||||||
|
else:
|
||||||
|
dispensable = list(all_shapes.geoms)
|
||||||
|
|
||||||
|
for i in range(len(polygons)):
|
||||||
|
polygons[i] = polygons[i].buffer(0)
|
||||||
|
|
||||||
|
logging.info(f"共生成多边形{len(polygons)}个,线段{len(dispensable)}条")
|
||||||
|
|
||||||
|
output = VirtualMachineOutput(polygons, dispensable)
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
class _Preprocessor(object):
|
||||||
|
'''PLT文件预处理器'''
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_abstract_syntax_tree(self, src_content: str) -> AbstractSyntaxTree:
|
||||||
|
content = src_content
|
||||||
|
|
||||||
|
# 换行符转分号 某些文件的语句末尾没有分号
|
||||||
|
content = content.replace("\n", ";")
|
||||||
|
content = content.replace(" ", ",")
|
||||||
|
|
||||||
|
# 删除所有空白字符
|
||||||
|
for space in string.whitespace:
|
||||||
|
content = content.replace(space, "")
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
# 遍历每一行命令
|
||||||
|
for line_index, line in enumerate(content.split(";")):
|
||||||
|
if line == "":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 提取开头的英文
|
||||||
|
pattern = r'^[a-zA-Z]+'
|
||||||
|
match = re.search(pattern, line)
|
||||||
|
|
||||||
|
if match is None:
|
||||||
|
assert_warning(False, f"[line {line_index}] 未找到命令: {line}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 获取对应的指令
|
||||||
|
command_str = match.group().upper()
|
||||||
|
try:
|
||||||
|
command = PltCommand[command_str]
|
||||||
|
except KeyError:
|
||||||
|
assert_warning(False, f"[line {line_index}] 不支持的指令: {command_str}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 获取参数
|
||||||
|
if len(command_str) == len(line):
|
||||||
|
stack = []
|
||||||
|
else:
|
||||||
|
stack = line[len(command_str):].split(",")
|
||||||
|
|
||||||
|
if command == PltCommand.IN:
|
||||||
|
result.append({'cmd': PltCommand.IN, 'args': []})
|
||||||
|
|
||||||
|
elif command == PltCommand.PA:
|
||||||
|
for i in range(len(stack) // 2):
|
||||||
|
result.append({'cmd': PltCommand.PA, 'args': [stack[0], stack[1]]})
|
||||||
|
stack = stack[2:]
|
||||||
|
|
||||||
|
elif command == PltCommand.PU:
|
||||||
|
result.append({'cmd': PltCommand.PU, 'args': []})
|
||||||
|
for i in range(len(stack) // 2):
|
||||||
|
result.append({'cmd': PltCommand.PA, 'args': [stack[0], stack[1]]})
|
||||||
|
stack = stack[2:]
|
||||||
|
|
||||||
|
elif command == PltCommand.PD:
|
||||||
|
result.append({'cmd': PltCommand.PD, 'args': []})
|
||||||
|
for i in range(len(stack) // 2):
|
||||||
|
result.append({'cmd': PltCommand.PA, 'args': [stack[0], stack[1]]})
|
||||||
|
stack = stack[2:]
|
||||||
|
|
||||||
|
assert_warning(len(stack) == 0, f"[line {line_index}] 栈非空: {stack}")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class PltReader(object):
|
||||||
|
'''PLT文件读取器'''
|
||||||
|
|
||||||
|
def __init__(self, pltfile: typing.TextIO) -> None:
|
||||||
|
self.pltfile = pltfile
|
||||||
|
|
||||||
|
logging.info("正在预处理PLT文件...")
|
||||||
|
preprocessor = _Preprocessor()
|
||||||
|
self.abstract_syntax_tree = preprocessor.get_abstract_syntax_tree(self.pltfile.read())
|
||||||
|
logging.info("预处理完成")
|
||||||
|
|
||||||
|
|
||||||
|
def get_output(self, tolerance=5, version: int = 2) -> VirtualMachineOutput:
|
||||||
|
machine = _VirtualMachine(tolerance=tolerance)
|
||||||
|
output = machine.run(self.abstract_syntax_tree, version)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
filepath = "标准.plt"
|
||||||
|
|
||||||
|
with open(filepath, 'r') as f:
|
||||||
|
reader = PltReader(f)
|
||||||
|
output = reader.get_output()
|
||||||
|
|
||||||
|
debug_full_sheet_path = "debug_full_sheet.png"
|
||||||
|
cv2.imwrite(debug_full_sheet_path, output.debug_full_sheet(scale_factor=72/1016))
|
||||||
|
print(f"调试整幅输出已写入{debug_full_sheet_path}")
|
||||||
|
|
||||||
|
full_sheet_path = "full_sheet.png"
|
||||||
|
cv2.imwrite(full_sheet_path, output.full_sheet(scale_factor=72/1016))
|
||||||
|
print(f"整幅输出已写入{full_sheet_path}")
|
||||||
|
|
||||||
|
single_size_path = "single_size"
|
||||||
|
for index, img in enumerate(output.single_size(size_num=5, scale_factor=72/1016)):
|
||||||
|
cv2.imwrite(os.path.join(single_size_path, f"{index}.png"), img)
|
||||||
|
print(f"单码输出已写入{single_size_path}")
|
||||||
|
|
||||||
|
single_piece_path = "single_piece"
|
||||||
|
for index, img in enumerate(output.single_piece(scale_factor=72/1016)):
|
||||||
|
cv2.imwrite(os.path.join(single_piece_path, f"{index}.png"), img)
|
||||||
|
print(f"单片输出已写入{single_piece_path}")
|
||||||
|
|
||||||
8
PltService/requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fastapi==0.109.0
|
||||||
|
uvicorn==0.27.0
|
||||||
|
python-multipart==0.0.6
|
||||||
|
numpy==1.26.3
|
||||||
|
opencv-python-headless==4.9.0.80
|
||||||
|
shapely==2.0.2
|
||||||
|
scipy==1.12.0
|
||||||
|
Pillow==10.2.0
|
||||||
1
Server/.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.14
|
||||||
340
Server/PLT_API_集成说明.md
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
# PLT 裁片处理 API 集成完成
|
||||||
|
|
||||||
|
## ✅ 已完成的工作
|
||||||
|
|
||||||
|
### 1. **核心文件创建**
|
||||||
|
- ✅ `Server/app/api/v1/algorithm.py` - PLT处理API接口
|
||||||
|
- ✅ `Server/pltreader.py` - 已复制到Server目录
|
||||||
|
- ✅ `Server/app/main.py` - 已注册algorithm路由
|
||||||
|
|
||||||
|
### 2. **依赖包更新**
|
||||||
|
- ✅ 添加 `scipy` - 用于匈牙利算法匹配
|
||||||
|
- ✅ 添加 `Pillow` - 用于图像处理和Base64编码
|
||||||
|
- ✅ 已有 `shapely`, `numpy`, `opencv-python-headless`
|
||||||
|
|
||||||
|
### 3. **API功能特性**
|
||||||
|
|
||||||
|
#### 核心功能
|
||||||
|
1. **PLT文件解析** - 自动识别裁片轮廓
|
||||||
|
2. **按尺码分组** - 自动匹配不同尺码的相同裁片
|
||||||
|
3. **生成透明PNG** - 每个裁片独立输出,带Alpha通道
|
||||||
|
4. **精确坐标计算** - 返回厘米单位的中心点和左上角坐标
|
||||||
|
5. **旋转支持** - 可指定0°/90°/-90°/180°旋转
|
||||||
|
6. **双文件匹配** - 可上传两个PLT文件进行轮廓对比
|
||||||
|
|
||||||
|
#### 安全特性
|
||||||
|
- ✅ JWT Token 认证保护
|
||||||
|
- ✅ 需要登录才能访问
|
||||||
|
- ✅ 集成现有用户系统
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 API 接口说明
|
||||||
|
|
||||||
|
### 接口地址
|
||||||
|
```
|
||||||
|
POST /api/v1/algorithm/process_plt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 请求方式
|
||||||
|
`multipart/form-data`
|
||||||
|
|
||||||
|
### 请求头
|
||||||
|
```
|
||||||
|
Authorization: Bearer <access_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 请求参数
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||||||
|
|--------|------|------|------|------|
|
||||||
|
| `file` | File | **是** | 标准PLT文件 | `standard.plt` |
|
||||||
|
| `rotated_file` | File | 否 | 旋转后的PLT文件(用于匹配分析) | `rotated.plt` |
|
||||||
|
| `size_labels` | String | **是** | 尺码标签JSON数组 | `["S","M","L","XL","2XL"]` |
|
||||||
|
| `dpi` | Integer | 否 | 输出分辨率(默认150) | `150` |
|
||||||
|
| `rotation` | Integer | 否 | 旋转角度(0/90/-90/180,默认0) | `90` |
|
||||||
|
|
||||||
|
### 响应示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"total_groups": 5,
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"group_id": 1,
|
||||||
|
"pieces": [
|
||||||
|
{
|
||||||
|
"size": "S",
|
||||||
|
"image_base64": "data:image/png;base64,iVBORw0KGgo...",
|
||||||
|
"width_px": 800,
|
||||||
|
"height_px": 600,
|
||||||
|
"width_cm": 13.54,
|
||||||
|
"height_cm": 10.16,
|
||||||
|
"center_x_cm": 25.4,
|
||||||
|
"center_y_cm": 30.2,
|
||||||
|
"left_cm": 18.63,
|
||||||
|
"top_cm": 35.28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"size": "M",
|
||||||
|
"image_base64": "data:image/png;base64,iVBORw0KGgo...",
|
||||||
|
"width_px": 820,
|
||||||
|
"height_px": 615,
|
||||||
|
"width_cm": 13.89,
|
||||||
|
"height_cm": 10.41,
|
||||||
|
"center_x_cm": 26.1,
|
||||||
|
"center_y_cm": 30.8,
|
||||||
|
"left_cm": 19.15,
|
||||||
|
"top_cm": 35.60
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match_analysis": [
|
||||||
|
{
|
||||||
|
"size": "S",
|
||||||
|
"matches": [
|
||||||
|
{
|
||||||
|
"standard_id": 0,
|
||||||
|
"rotated_id": 3,
|
||||||
|
"distance": 0.7032,
|
||||||
|
"angle": 0.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 测试步骤
|
||||||
|
|
||||||
|
### 1. 安装依赖
|
||||||
|
```bash
|
||||||
|
cd Server
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 启动服务
|
||||||
|
```bash
|
||||||
|
cd Server
|
||||||
|
python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 运行测试脚本
|
||||||
|
```bash
|
||||||
|
cd Server
|
||||||
|
python test_plt_api.py
|
||||||
|
```
|
||||||
|
|
||||||
|
测试脚本会自动:
|
||||||
|
1. 登录获取Token
|
||||||
|
2. 上传PLT文件进行处理
|
||||||
|
3. 显示匹配结果
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 技术实现细节
|
||||||
|
|
||||||
|
### 1. **裁片识别算法**
|
||||||
|
- 使用 Shapely 进行几何计算
|
||||||
|
- 自动构建多边形包含关系树
|
||||||
|
- 过滤噪声(面积阈值:0.68%² ~ 80%²)
|
||||||
|
|
||||||
|
### 2. **尺码匹配算法**
|
||||||
|
- **Hausdorff距离** - 衡量轮廓相似度
|
||||||
|
- **匈牙利算法** - 最优匹配分配
|
||||||
|
- 自动处理旋转(0°/90°/180°/270°)
|
||||||
|
- 质心对齐 + 面积归一化
|
||||||
|
|
||||||
|
### 3. **坐标系统**
|
||||||
|
- PLT单位:1016单位 = 1英寸 = 2.54cm
|
||||||
|
- 原点:左下角
|
||||||
|
- Y轴:向上为正(数学坐标系)
|
||||||
|
|
||||||
|
### 4. **图像处理**
|
||||||
|
- OpenCV绘制裁片轮廓
|
||||||
|
- 自动添加Alpha通道(透明背景)
|
||||||
|
- Pillow处理旋转和Base64编码
|
||||||
|
- DPI元数据写入PNG文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 使用示例 (Python)
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
# 1. 登录
|
||||||
|
login_response = requests.post(
|
||||||
|
"http://localhost:8000/api/v1/auth/login",
|
||||||
|
json={
|
||||||
|
"username": "admin",
|
||||||
|
"password": "123456",
|
||||||
|
"device_id": "test-device"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
token = login_response.json()["access_token"]
|
||||||
|
|
||||||
|
# 2. 处理PLT文件
|
||||||
|
with open("standard.plt", "rb") as f1:
|
||||||
|
files = [
|
||||||
|
("file", ("standard.plt", f1, "application/octet-stream"))
|
||||||
|
]
|
||||||
|
data = {
|
||||||
|
"size_labels": json.dumps(["S", "M", "L", "XL", "2XL"]),
|
||||||
|
"dpi": 150,
|
||||||
|
"rotation": 90 # 顺时针旋转90度
|
||||||
|
}
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
"http://localhost:8000/api/v1/algorithm/process_plt",
|
||||||
|
files=files,
|
||||||
|
data=data,
|
||||||
|
headers=headers
|
||||||
|
)
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
print(f"共生成 {result['total_groups']} 组裁片")
|
||||||
|
|
||||||
|
# 3. 保存图片
|
||||||
|
for group in result["groups"]:
|
||||||
|
for piece in group["pieces"]:
|
||||||
|
# 提取Base64数据
|
||||||
|
base64_data = piece["image_base64"].split(",")[1]
|
||||||
|
|
||||||
|
# 解码并保存
|
||||||
|
import base64
|
||||||
|
img_data = base64.b64decode(base64_data)
|
||||||
|
with open(f"{piece['size']}-{group['group_id']}.png", "wb") as img_file:
|
||||||
|
img_file.write(img_data)
|
||||||
|
|
||||||
|
print(f"已保存: {piece['size']}-{group['group_id']}.png")
|
||||||
|
print(f" 位置: ({piece['left_cm']}, {piece['top_cm']}) cm")
|
||||||
|
print(f" 尺寸: {piece['width_cm']} x {piece['height_cm']} cm")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Photoshop插件集成要点
|
||||||
|
|
||||||
|
### 1. **创建画布**
|
||||||
|
```javascript
|
||||||
|
// 根据API返回的尺寸创建画布
|
||||||
|
var canvasWidth = 100; // cm,根据实际需求
|
||||||
|
var canvasHeight = 150; // cm
|
||||||
|
var dpi = 150;
|
||||||
|
|
||||||
|
var doc = app.documents.add(
|
||||||
|
UnitValue(canvasWidth, "cm"),
|
||||||
|
UnitValue(canvasHeight, "cm"),
|
||||||
|
dpi,
|
||||||
|
"PLT裁片",
|
||||||
|
NewDocumentMode.RGB,
|
||||||
|
DocumentFill.TRANSPARENT
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **加载Base64图片**
|
||||||
|
```javascript
|
||||||
|
function loadBase64Image(base64String, layerName) {
|
||||||
|
// 去除前缀
|
||||||
|
var base64Data = base64String.split(",")[1];
|
||||||
|
|
||||||
|
// 保存为临时文件
|
||||||
|
var tempFile = new File(Folder.temp + "/temp_piece.png");
|
||||||
|
tempFile.encoding = "BINARY";
|
||||||
|
tempFile.open("w");
|
||||||
|
tempFile.write(decode64(base64Data));
|
||||||
|
tempFile.close();
|
||||||
|
|
||||||
|
// 打开并复制到主文档
|
||||||
|
var tempDoc = app.open(tempFile);
|
||||||
|
tempDoc.activeLayer.copy();
|
||||||
|
app.activeDocument = mainDoc;
|
||||||
|
var newLayer = mainDoc.paste();
|
||||||
|
newLayer.name = layerName;
|
||||||
|
tempDoc.close(SaveOptions.DONOTSAVECHANGES);
|
||||||
|
tempFile.remove();
|
||||||
|
|
||||||
|
return newLayer;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **定位图层**
|
||||||
|
```javascript
|
||||||
|
function positionLayer(layer, leftCm, topCm, dpi) {
|
||||||
|
// 厘米转像素
|
||||||
|
var leftPx = leftCm * dpi / 2.54;
|
||||||
|
var topPx = topCm * dpi / 2.54;
|
||||||
|
|
||||||
|
// 移动到指定位置
|
||||||
|
var deltaX = leftPx - layer.bounds[0];
|
||||||
|
var deltaY = topPx - layer.bounds[1];
|
||||||
|
layer.translate(deltaX, deltaY);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
1. **文件编码** - PLT文件必须是文本格式,如果是二进制格式会导致解析失败
|
||||||
|
2. **尺码数量** - `size_labels` 的数量必须与PLT文件中的实际尺码数量一致
|
||||||
|
3. **内存占用** - 处理大型PLT文件(>1000个裁片)可能需要较长时间
|
||||||
|
4. **坐标系** - Photoshop的Y轴向下,如需适配可能需要翻转Y坐标
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 故障排查
|
||||||
|
|
||||||
|
### 问题1: 导入错误 `cannot import name 'algorithm'`
|
||||||
|
**解决**:
|
||||||
|
```bash
|
||||||
|
# 确保已重启uvicorn服务
|
||||||
|
python -m uvicorn app.main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
### 问题2: `ModuleNotFoundError: No module named 'scipy'`
|
||||||
|
**解决**:
|
||||||
|
```bash
|
||||||
|
pip install scipy Pillow
|
||||||
|
```
|
||||||
|
|
||||||
|
### 问题3: PLT文件解析失败
|
||||||
|
**检查**:
|
||||||
|
- 文件是否是文本格式(用记事本能打开)
|
||||||
|
- 文件编码是否正确(建议UTF-8或GBK)
|
||||||
|
- 文件是否包含有效的PLT命令(IN, PU, PD, PA等)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 相关文档
|
||||||
|
|
||||||
|
- `Server/docs/API_PLT_Processing.md` - 详细API文档
|
||||||
|
- `Server/test_plt_api.py` - 测试示例代码
|
||||||
|
- `pltreader.py` - PLT解析器源码
|
||||||
|
- `run.py` - 命令行工具示例
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎉 完成!
|
||||||
|
|
||||||
|
现在你可以启动服务器并测试API了:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd D:\main\DesignerCEP\Server
|
||||||
|
python -m uvicorn app.main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
然后在浏览器访问文档:
|
||||||
|
```
|
||||||
|
http://localhost:8000/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
祝使用愉快!如有问题请随时反馈。
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException, status, Form
|
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException, status, Form, Header
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.models.group import PluginGroup as DBPluginGroup
|
from app.models.group import PluginGroup as DBPluginGroup
|
||||||
@@ -8,21 +8,21 @@ from app.models.user import User
|
|||||||
from app.schemas.group import PluginGroupCreate, PluginGroupUpdate, PluginGroup
|
from app.schemas.group import PluginGroupCreate, PluginGroupUpdate, PluginGroup
|
||||||
from app.schemas.admin import UserInfo
|
from app.schemas.admin import UserInfo
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
# Hardcoded admin token for simplicity as per requirements
|
# 从配置读取管理员令牌
|
||||||
ADMIN_TOKEN = "admin-secret-token"
|
ADMIN_TOKEN = settings.ADMIN_TOKEN
|
||||||
|
|
||||||
def verify_admin(token: str = Form(...)):
|
def verify_admin(token: str = Form(...)):
|
||||||
if token != ADMIN_TOKEN:
|
if token != ADMIN_TOKEN:
|
||||||
raise HTTPException(status_code=403, detail="Admin permission required")
|
raise HTTPException(status_code=403, detail="需要管理员权限")
|
||||||
|
|
||||||
def get_admin_dep(x_admin_token: str = None):
|
def verify_admin_header(x_admin_token: Optional[str] = Header(None, alias="X-Admin-Token")):
|
||||||
# Alternative using header
|
"""通过 Header 验证管理员身份"""
|
||||||
if x_admin_token != ADMIN_TOKEN:
|
if x_admin_token != ADMIN_TOKEN:
|
||||||
raise HTTPException(status_code=403, detail="Admin permission required")
|
raise HTTPException(status_code=403, detail="需要管理员权限")
|
||||||
|
|
||||||
# Ensure archives directory exists
|
# Ensure archives directory exists
|
||||||
ARCHIVES_DIR = "archives"
|
ARCHIVES_DIR = "archives"
|
||||||
@@ -31,11 +31,11 @@ os.makedirs(ARCHIVES_DIR, exist_ok=True)
|
|||||||
@router.post("/upload_version")
|
@router.post("/upload_version")
|
||||||
async def upload_version(
|
async def upload_version(
|
||||||
file: UploadFile = File(...),
|
file: UploadFile = File(...),
|
||||||
# token: str = Form(...), # Simple auth
|
token: str = Form(...),
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
# if token != ADMIN_TOKEN:
|
if token != ADMIN_TOKEN:
|
||||||
# raise HTTPException(status_code=403, detail="Invalid admin token")
|
raise HTTPException(status_code=403, detail="Invalid admin token")
|
||||||
|
|
||||||
file_location = os.path.join(ARCHIVES_DIR, file.filename)
|
file_location = os.path.join(ARCHIVES_DIR, file.filename)
|
||||||
with open(file_location, "wb+") as file_object:
|
with open(file_location, "wb+") as file_object:
|
||||||
@@ -44,7 +44,7 @@ async def upload_version(
|
|||||||
return {"code": 200, "message": f"File '{file.filename}' uploaded successfully", "filename": file.filename}
|
return {"code": 200, "message": f"File '{file.filename}' uploaded successfully", "filename": file.filename}
|
||||||
|
|
||||||
@router.get("/archives")
|
@router.get("/archives")
|
||||||
async def list_archives():
|
async def list_archives(_: None = Depends(verify_admin_header)):
|
||||||
if not os.path.exists(ARCHIVES_DIR):
|
if not os.path.exists(ARCHIVES_DIR):
|
||||||
return []
|
return []
|
||||||
files = os.listdir(ARCHIVES_DIR)
|
files = os.listdir(ARCHIVES_DIR)
|
||||||
@@ -53,7 +53,7 @@ async def list_archives():
|
|||||||
return files
|
return files
|
||||||
|
|
||||||
@router.post("/groups", response_model=PluginGroup)
|
@router.post("/groups", response_model=PluginGroup)
|
||||||
async def create_group(group: PluginGroupCreate, db: Session = Depends(get_db)):
|
async def create_group(group: PluginGroupCreate, db: Session = Depends(get_db), _: None = Depends(verify_admin_header)):
|
||||||
db_group = DBPluginGroup(**group.model_dump())
|
db_group = DBPluginGroup(**group.model_dump())
|
||||||
db.add(db_group)
|
db.add(db_group)
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -61,11 +61,11 @@ async def create_group(group: PluginGroupCreate, db: Session = Depends(get_db)):
|
|||||||
return db_group
|
return db_group
|
||||||
|
|
||||||
@router.get("/groups", response_model=List[PluginGroup])
|
@router.get("/groups", response_model=List[PluginGroup])
|
||||||
async def list_groups(db: Session = Depends(get_db)):
|
async def list_groups(db: Session = Depends(get_db), _: None = Depends(verify_admin_header)):
|
||||||
return db.query(DBPluginGroup).all()
|
return db.query(DBPluginGroup).all()
|
||||||
|
|
||||||
@router.put("/groups/{group_id}", response_model=PluginGroup)
|
@router.put("/groups/{group_id}", response_model=PluginGroup)
|
||||||
async def update_group(group_id: int, group_update: PluginGroupUpdate, db: Session = Depends(get_db)):
|
async def update_group(group_id: int, group_update: PluginGroupUpdate, db: Session = Depends(get_db), _: None = Depends(verify_admin_header)):
|
||||||
db_group = db.query(DBPluginGroup).filter(DBPluginGroup.id == group_id).first()
|
db_group = db.query(DBPluginGroup).filter(DBPluginGroup.id == group_id).first()
|
||||||
if not db_group:
|
if not db_group:
|
||||||
raise HTTPException(status_code=404, detail="Group not found")
|
raise HTTPException(status_code=404, detail="Group not found")
|
||||||
@@ -79,11 +79,11 @@ async def update_group(group_id: int, group_update: PluginGroupUpdate, db: Sessi
|
|||||||
return db_group
|
return db_group
|
||||||
|
|
||||||
@router.get("/users", response_model=List[UserInfo])
|
@router.get("/users", response_model=List[UserInfo])
|
||||||
async def list_users(db: Session = Depends(get_db)):
|
async def list_users(db: Session = Depends(get_db), _: None = Depends(verify_admin_header)):
|
||||||
return db.query(User).all()
|
return db.query(User).all()
|
||||||
|
|
||||||
@router.put("/users/{user_id}/group")
|
@router.put("/users/{user_id}/group")
|
||||||
async def update_user_group(user_id: int, group_id: int, db: Session = Depends(get_db)):
|
async def update_user_group(user_id: int, group_id: int, db: Session = Depends(get_db), _: None = Depends(verify_admin_header)):
|
||||||
user = db.query(User).filter(User.id == user_id).first()
|
user = db.query(User).filter(User.id == user_id).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="User not found")
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
@@ -97,7 +97,7 @@ async def update_user_group(user_id: int, group_id: int, db: Session = Depends(g
|
|||||||
return {"code": 200, "message": "User group updated"}
|
return {"code": 200, "message": "User group updated"}
|
||||||
|
|
||||||
@router.put("/users/{user_id}/permissions")
|
@router.put("/users/{user_id}/permissions")
|
||||||
async def update_user_permissions(user_id: int, permissions: str = Form(...), db: Session = Depends(get_db)):
|
async def update_user_permissions(user_id: int, permissions: str = Form(...), db: Session = Depends(get_db), _: None = Depends(verify_admin_header)):
|
||||||
user = db.query(User).filter(User.id == user_id).first()
|
user = db.query(User).filter(User.id == user_id).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="User not found")
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from pydantic import BaseModel
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.models.business import FeatureConfig, VipConfig, CheckInConfig
|
from app.models.business import FeatureConfig, VipConfig, CheckInConfig
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -51,8 +52,7 @@ class CheckInConfigUpdate(BaseModel):
|
|||||||
|
|
||||||
def verify_admin_token(token: str):
|
def verify_admin_token(token: str):
|
||||||
"""验证管理员Token"""
|
"""验证管理员Token"""
|
||||||
expected_token = "admin-secret-token"
|
if token != settings.ADMIN_TOKEN:
|
||||||
if token != expected_token:
|
|
||||||
raise HTTPException(status_code=401, detail="管理员Token无效")
|
raise HTTPException(status_code=401, detail="管理员Token无效")
|
||||||
|
|
||||||
# ==================== 功能配置管理 ====================
|
# ==================== 功能配置管理 ====================
|
||||||
|
|||||||
1270
Server/app/api/v1/ai_chat.py
Normal file
239
Server/app/api/v1/ai_tools.py
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
AI 工具定义
|
||||||
|
将 PS 操作注册为 function calling 的 tool schema
|
||||||
|
前端收到 tool_calls 后执行 JSX,把结果回传
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ==================== 工具 Schema(OpenAI function calling 格式)====================
|
||||||
|
|
||||||
|
PS_TOOLS = [
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "get_document_info",
|
||||||
|
"description": "获取当前 Photoshop 文档信息(名称、尺寸、分辨率)",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "get_layer_structure",
|
||||||
|
"description": "获取当前文档的图层结构树(所有顶层组和子图层)",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "create_layer",
|
||||||
|
"description": "在 Photoshop 中创建新图层",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "新图层的名称"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "rename_layer",
|
||||||
|
"description": "重命名当前选中的图层",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"new_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "新名称"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["new_name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "delete_layer",
|
||||||
|
"description": "删除当前选中的图层",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "duplicate_layer",
|
||||||
|
"description": "复制当前选中的图层",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "align_layers",
|
||||||
|
"description": "对齐当前选中的图层",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["left", "right", "centerH", "top", "bottom", "centerV"],
|
||||||
|
"description": "对齐方式:left=左对齐, right=右对齐, centerH=水平居中, top=顶对齐, bottom=底对齐, centerV=垂直居中"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "merge_layers",
|
||||||
|
"description": "合并当前选中的图层",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "create_layer_group",
|
||||||
|
"description": "创建图层组(如果不存在)",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "图层组名称"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "move_layer_to_group",
|
||||||
|
"description": "将当前图层移入指定图层组",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"group_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "目标图层组名称"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["group_name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
# ==================== 套图工具 ====================
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "identify_pieces",
|
||||||
|
"description": "识别裁片部位。截取当前PS画布并分析每个裁片图层的形状和位置,用视觉AI识别哪个图层是前片、后片、袖子等。应在套图前先调用,以建立图层名到裁片部位的对应关系。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "generate_garment_preview",
|
||||||
|
"description": "【阶段1-生成预览】将用户上传的成衣照片中的花样,填充到 PS 文档的裁片轮廓上,生成一张带轮廓的花样预览图。预览图会显示在聊天中,供用户确认效果。需要用户已上传成衣图片。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "extract_and_apply_all_pieces",
|
||||||
|
"description": "【阶段2-提取套图】用户确认预览 OK 后调用。根据 identify_pieces 的分析结果,对每个裁片执行不同操作:solid=PS纯色填充;fill_pattern=AI提取花型铺满;theme_pattern=底层纯色填充+上层AI提取主题图案(白底+正片叠底);mixed_pattern=底层AI提取花型+上层AI提取主题图案(白底+正片叠底)。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"pieces": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {"type": "string", "description": "裁片图层名称"},
|
||||||
|
"type": {"type": "string", "enum": ["solid", "fill_pattern", "theme_pattern", "mixed_pattern"], "description": "图案类型:solid=纯色;fill_pattern=花型铺满;theme_pattern=主题图案+纯色底;mixed_pattern=主题图案+花型底纹"},
|
||||||
|
"color": {"type": "string", "description": "颜色 hex 值。solid 和 theme_pattern 必须提供(theme_pattern 的 color 是底色)"},
|
||||||
|
"description": {"type": "string", "description": "图案内容描述"}
|
||||||
|
},
|
||||||
|
"required": ["name", "type"]
|
||||||
|
},
|
||||||
|
"description": "裁片列表,type 和 color 来自 identify_pieces 的分析结果"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["pieces"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "verify_pattern_result",
|
||||||
|
"description": "验证套图效果。自动截取当前 PS 画布与原始成衣对比,由视觉 AI 评分(1-10)并给出改进建议。应在套图完成后调用。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# 工具名称映射(中文显示用)
|
||||||
|
TOOL_DISPLAY_NAMES = {
|
||||||
|
"get_document_info": "查看文档信息",
|
||||||
|
"get_layer_structure": "查看图层结构",
|
||||||
|
"create_layer": "新建图层",
|
||||||
|
"rename_layer": "重命名图层",
|
||||||
|
"delete_layer": "删除图层",
|
||||||
|
"duplicate_layer": "复制图层",
|
||||||
|
"align_layers": "对齐图层",
|
||||||
|
"merge_layers": "合并图层",
|
||||||
|
"create_layer_group": "创建图层组",
|
||||||
|
"move_layer_to_group": "移入图层组",
|
||||||
|
"identify_pieces": "识别裁片部位",
|
||||||
|
"generate_garment_preview": "生成花样预览",
|
||||||
|
"extract_and_apply_all_pieces": "提取并套图",
|
||||||
|
"verify_pattern_result": "验证套图效果",
|
||||||
|
}
|
||||||
506
Server/app/api/v1/algorithm.py
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
PLT 裁片处理接口 (Photoshop 插件专用)
|
||||||
|
功能:解析 PLT 文件,生成裁片图片和坐标信息
|
||||||
|
优化版:提升匹配计算速度 + 并行图片生成
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import io
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import numpy as np
|
||||||
|
import cv2
|
||||||
|
from typing import List, Optional, Dict, Tuple
|
||||||
|
from fastapi import APIRouter, UploadFile, File, Form, HTTPException, Depends
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from PIL import Image
|
||||||
|
from shapely import affinity
|
||||||
|
from scipy.optimize import linear_sum_assignment
|
||||||
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# 导入自定义模块
|
||||||
|
import sys
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
|
||||||
|
from pltreader import PltReader
|
||||||
|
|
||||||
|
from app.db import get_db
|
||||||
|
from app.core.security import get_current_user
|
||||||
|
from app.core.qiniu_storage import qiniu_storage
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
# ==================== 数据模型 ====================
|
||||||
|
|
||||||
|
class PieceInfo(BaseModel):
|
||||||
|
"""单个裁片信息"""
|
||||||
|
size: str
|
||||||
|
image_base64: Optional[str] = None # base64 图片(未启用云存储时使用)
|
||||||
|
image_url: Optional[str] = None # 云存储 URL(启用七牛云时使用)
|
||||||
|
width_px: int
|
||||||
|
height_px: int
|
||||||
|
width_cm: float
|
||||||
|
height_cm: float
|
||||||
|
center_x_cm: float
|
||||||
|
center_y_cm: float
|
||||||
|
left_cm: float
|
||||||
|
top_cm: float
|
||||||
|
|
||||||
|
class GroupInfo(BaseModel):
|
||||||
|
"""裁片分组信息"""
|
||||||
|
group_id: int
|
||||||
|
pieces: List[PieceInfo]
|
||||||
|
|
||||||
|
class MatchInfo(BaseModel):
|
||||||
|
"""双文件匹配结果"""
|
||||||
|
standard_id: int
|
||||||
|
rotated_id: int
|
||||||
|
distance: float
|
||||||
|
angle: float
|
||||||
|
|
||||||
|
class SizeMatchInfo(BaseModel):
|
||||||
|
"""每个尺码的匹配结果"""
|
||||||
|
size: str
|
||||||
|
matches: List[MatchInfo]
|
||||||
|
|
||||||
|
class ProcessPltResponse(BaseModel):
|
||||||
|
"""API 响应"""
|
||||||
|
success: bool
|
||||||
|
total_groups: int
|
||||||
|
groups: List[GroupInfo]
|
||||||
|
match_analysis: Optional[List[SizeMatchInfo]] = None
|
||||||
|
|
||||||
|
# ==================== 优化的辅助函数 ====================
|
||||||
|
|
||||||
|
def parse_plt_file(file_content: str, tolerance: int = 10):
|
||||||
|
"""解析 PLT 文件内容"""
|
||||||
|
reader = PltReader(io.StringIO(file_content))
|
||||||
|
return reader.get_output(tolerance=tolerance)
|
||||||
|
|
||||||
|
def normalize_polygon(poly):
|
||||||
|
"""
|
||||||
|
预处理多边形:质心对齐 + 面积归一化
|
||||||
|
返回: (归一化多边形, 原始面积, 原始周长)
|
||||||
|
"""
|
||||||
|
area = poly.area
|
||||||
|
perimeter = poly.length
|
||||||
|
|
||||||
|
if area <= 0:
|
||||||
|
return poly, 0, 0
|
||||||
|
|
||||||
|
# 质心对齐
|
||||||
|
poly = affinity.translate(poly, xoff=-poly.centroid.x, yoff=-poly.centroid.y)
|
||||||
|
|
||||||
|
# 缩放到统一面积
|
||||||
|
target_area = 10000
|
||||||
|
scale = (target_area / area) ** 0.5
|
||||||
|
poly = affinity.scale(poly, xfact=scale, yfact=scale, origin=(0, 0))
|
||||||
|
|
||||||
|
return poly, area, perimeter
|
||||||
|
|
||||||
|
def fast_shape_similarity(poly1_data: Tuple, poly2_data: Tuple) -> Tuple[float, int]:
|
||||||
|
"""
|
||||||
|
快速形状相似度计算
|
||||||
|
poly_data: (归一化多边形, 原始面积, 原始周长)
|
||||||
|
返回: (距离, 最佳角度)
|
||||||
|
"""
|
||||||
|
poly1, area1, peri1 = poly1_data
|
||||||
|
poly2, area2, peri2 = poly2_data
|
||||||
|
|
||||||
|
# 面积比初筛(面积差异太大的直接跳过)
|
||||||
|
if area1 > 0 and area2 > 0:
|
||||||
|
area_ratio = min(area1, area2) / max(area1, area2)
|
||||||
|
if area_ratio < 0.5: # 面积差异超过50%
|
||||||
|
return float('inf'), 0
|
||||||
|
|
||||||
|
# 只尝试 0° 和 180°(服装裁片通常只需要这两个角度)
|
||||||
|
dist_min = float('inf')
|
||||||
|
best_angle = 0
|
||||||
|
|
||||||
|
for angle in [0, 180]:
|
||||||
|
try:
|
||||||
|
rotated = affinity.rotate(poly2, angle, origin='centroid') if angle != 0 else poly2
|
||||||
|
# 使用 symmetric_difference 面积作为距离度量(比 hausdorff 快很多)
|
||||||
|
diff_area = poly1.symmetric_difference(rotated).area
|
||||||
|
dist = diff_area / max(poly1.area, 1)
|
||||||
|
except:
|
||||||
|
dist = float('inf')
|
||||||
|
|
||||||
|
if dist < dist_min:
|
||||||
|
dist_min = dist
|
||||||
|
best_angle = angle
|
||||||
|
|
||||||
|
return dist_min, best_angle
|
||||||
|
|
||||||
|
def batch_normalize_polygons(pieces: List[Dict]) -> List[Tuple]:
|
||||||
|
"""批量预处理多边形"""
|
||||||
|
return [normalize_polygon(p["data"]) for p in pieces]
|
||||||
|
|
||||||
|
def compute_matching_matrix(base_polys: List[Tuple], compare_polys: List[Tuple]) -> np.ndarray:
|
||||||
|
"""计算匹配成本矩阵"""
|
||||||
|
n, m = len(base_polys), len(compare_polys)
|
||||||
|
cost_matrix = np.zeros((n, m))
|
||||||
|
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(m):
|
||||||
|
dist, _ = fast_shape_similarity(base_polys[i], compare_polys[j])
|
||||||
|
cost_matrix[i, j] = dist
|
||||||
|
|
||||||
|
return cost_matrix
|
||||||
|
|
||||||
|
def apply_rotation_to_image(pil_img: Image.Image, rotation: int) -> Image.Image:
|
||||||
|
"""应用旋转变换"""
|
||||||
|
if rotation == 90:
|
||||||
|
return pil_img.rotate(-90, expand=True)
|
||||||
|
elif rotation == -90:
|
||||||
|
return pil_img.rotate(90, expand=True)
|
||||||
|
elif rotation == 180:
|
||||||
|
return pil_img.rotate(180, expand=True)
|
||||||
|
return pil_img
|
||||||
|
|
||||||
|
def calculate_piece_coordinates(polygon, bounds, plt_to_cm, rotation=0) -> Dict:
|
||||||
|
"""计算裁片坐标(厘米单位)"""
|
||||||
|
min_x, min_y, max_x, max_y = bounds
|
||||||
|
|
||||||
|
centroid = polygon.centroid
|
||||||
|
orig_center_x = (centroid.x - min_x) * plt_to_cm
|
||||||
|
orig_center_y = (centroid.y - min_y) * plt_to_cm
|
||||||
|
|
||||||
|
piece_bounds = polygon.bounds
|
||||||
|
orig_piece_width = (piece_bounds[2] - piece_bounds[0]) * plt_to_cm
|
||||||
|
orig_piece_height = (piece_bounds[3] - piece_bounds[1]) * plt_to_cm
|
||||||
|
|
||||||
|
orig_canvas_width = (max_x - min_x) * plt_to_cm
|
||||||
|
orig_canvas_height = (max_y - min_y) * plt_to_cm
|
||||||
|
|
||||||
|
if rotation == 90:
|
||||||
|
center_x = orig_center_y
|
||||||
|
center_y = orig_canvas_width - orig_center_x
|
||||||
|
piece_width, piece_height = orig_piece_height, orig_piece_width
|
||||||
|
elif rotation == -90:
|
||||||
|
center_x = orig_canvas_height - orig_center_y
|
||||||
|
center_y = orig_center_x
|
||||||
|
piece_width, piece_height = orig_piece_height, orig_piece_width
|
||||||
|
elif rotation == 180:
|
||||||
|
center_x = orig_canvas_width - orig_center_x
|
||||||
|
center_y = orig_canvas_height - orig_center_y
|
||||||
|
piece_width, piece_height = orig_piece_width, orig_piece_height
|
||||||
|
else:
|
||||||
|
center_x, center_y = orig_center_x, orig_center_y
|
||||||
|
piece_width, piece_height = orig_piece_width, orig_piece_height
|
||||||
|
|
||||||
|
left_cm = center_x - piece_width / 2
|
||||||
|
top_cm = center_y - piece_height / 2
|
||||||
|
|
||||||
|
return {
|
||||||
|
"center_x_cm": round(center_x, 2),
|
||||||
|
"center_y_cm": round(center_y, 2),
|
||||||
|
"left_cm": round(left_cm, 2),
|
||||||
|
"top_cm": round(top_cm, 2),
|
||||||
|
"width_cm": round(piece_width, 2),
|
||||||
|
"height_cm": round(piece_height, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_size_matching(clusters: List, size_num: int) -> Dict[int, List[int]]:
|
||||||
|
"""
|
||||||
|
获取尺码间匹配关系(优化版)
|
||||||
|
返回: {base_index: [matched_indices_per_size]}
|
||||||
|
"""
|
||||||
|
# 提取各尺码的父节点
|
||||||
|
size_parents = []
|
||||||
|
for cluster in clusters:
|
||||||
|
parents = [p for p in cluster if p["parent"] is None]
|
||||||
|
size_parents.append(parents)
|
||||||
|
|
||||||
|
# 预处理第一个尺码的多边形
|
||||||
|
base_pieces = size_parents[0]
|
||||||
|
base_polys = batch_normalize_polygons(base_pieces)
|
||||||
|
|
||||||
|
# 初始化匹配结果
|
||||||
|
match_result = {p["index"]: [p["index"]] for p in base_pieces}
|
||||||
|
|
||||||
|
# 依次匹配其他尺码
|
||||||
|
for size_idx in range(1, len(size_parents)):
|
||||||
|
compare_pieces = size_parents[size_idx]
|
||||||
|
compare_polys = batch_normalize_polygons(compare_pieces)
|
||||||
|
|
||||||
|
# 计算成本矩阵
|
||||||
|
cost_matrix = compute_matching_matrix(base_polys, compare_polys)
|
||||||
|
|
||||||
|
# 匈牙利算法匹配
|
||||||
|
row_ind, col_ind = linear_sum_assignment(cost_matrix)
|
||||||
|
|
||||||
|
for i, j in zip(row_ind, col_ind):
|
||||||
|
base_index = base_pieces[i]['index']
|
||||||
|
matched_index = compare_pieces[j]['index']
|
||||||
|
match_result[base_index].append(matched_index)
|
||||||
|
|
||||||
|
return match_result
|
||||||
|
|
||||||
|
def generate_single_piece_image(args: Tuple) -> Dict:
|
||||||
|
"""生成单个裁片图片(用于并行处理)"""
|
||||||
|
output, node, scale_factor, rotation, dpi, size_label, group_id, global_bounds, plt_to_cm = args
|
||||||
|
|
||||||
|
try:
|
||||||
|
nodes_to_draw = [node] + node['child']
|
||||||
|
|
||||||
|
# 绘制图像
|
||||||
|
img = output._draw_nodes(nodes_to_draw, scale_factor, show_id=False)
|
||||||
|
img_rgba = cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA)
|
||||||
|
pil_img = Image.fromarray(img_rgba)
|
||||||
|
|
||||||
|
# 应用旋转
|
||||||
|
pil_img = apply_rotation_to_image(pil_img, rotation)
|
||||||
|
|
||||||
|
# 转换为 Base64
|
||||||
|
buffer = io.BytesIO()
|
||||||
|
pil_img.save(buffer, format='PNG', dpi=(dpi, dpi))
|
||||||
|
buffer.seek(0)
|
||||||
|
base64_str = base64.b64encode(buffer.read()).decode('utf-8')
|
||||||
|
image_base64 = f"data:image/png;base64,{base64_str}"
|
||||||
|
|
||||||
|
# 计算坐标
|
||||||
|
coords = calculate_piece_coordinates(node["data"], global_bounds, plt_to_cm, rotation)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"group_id": group_id,
|
||||||
|
"size_label": size_label,
|
||||||
|
"image_base64": image_base64,
|
||||||
|
"width_px": pil_img.width,
|
||||||
|
"height_px": pil_img.height,
|
||||||
|
**coords
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
return {"success": False, "error": str(e), "group_id": group_id, "size_label": size_label}
|
||||||
|
|
||||||
|
def get_match_result_between_files(standard_clusters, rotated_clusters) -> List[List[Dict]]:
|
||||||
|
"""计算两个文件间的匹配结果(优化版)"""
|
||||||
|
all_size_matches = []
|
||||||
|
|
||||||
|
for standard_cluster, rotated_cluster in zip(standard_clusters, rotated_clusters):
|
||||||
|
standard_parents = [p for p in standard_cluster if p["parent"] is None]
|
||||||
|
rotated_parents = [p for p in rotated_cluster if p["parent"] is None]
|
||||||
|
|
||||||
|
if len(standard_parents) != len(rotated_parents):
|
||||||
|
continue
|
||||||
|
|
||||||
|
n = len(standard_parents)
|
||||||
|
|
||||||
|
# 批量预处理
|
||||||
|
std_polys = batch_normalize_polygons(standard_parents)
|
||||||
|
rot_polys = batch_normalize_polygons(rotated_parents)
|
||||||
|
|
||||||
|
# 计算成本矩阵和角度矩阵
|
||||||
|
cost_matrix = np.zeros((n, n))
|
||||||
|
rotation_matrix = np.zeros((n, n))
|
||||||
|
|
||||||
|
for i in range(n):
|
||||||
|
for j in range(n):
|
||||||
|
dist, angle = fast_shape_similarity(std_polys[i], rot_polys[j])
|
||||||
|
cost_matrix[i, j] = dist
|
||||||
|
rotation_matrix[i, j] = angle
|
||||||
|
|
||||||
|
# 匈牙利算法匹配
|
||||||
|
row_ind, col_ind = linear_sum_assignment(cost_matrix)
|
||||||
|
|
||||||
|
matches = []
|
||||||
|
for i, j in zip(row_ind, col_ind):
|
||||||
|
matches.append({
|
||||||
|
"standard_id": standard_parents[i]["index"],
|
||||||
|
"rotated_id": rotated_parents[j]["index"],
|
||||||
|
"distance": round(cost_matrix[i, j], 4),
|
||||||
|
"angle": rotation_matrix[i, j]
|
||||||
|
})
|
||||||
|
|
||||||
|
all_size_matches.append(matches)
|
||||||
|
|
||||||
|
return all_size_matches
|
||||||
|
|
||||||
|
# ==================== API 接口 ====================
|
||||||
|
|
||||||
|
@router.post("/algorithm/process_plt", response_model=ProcessPltResponse)
|
||||||
|
async def process_plt(
|
||||||
|
file: UploadFile = File(..., description="标准 PLT 文件"),
|
||||||
|
rotated_file: Optional[UploadFile] = File(None, description="旋转后的 PLT 文件(可选)"),
|
||||||
|
size_labels: str = Form(..., description="尺码标签 JSON 数组"),
|
||||||
|
dpi: int = Form(150, description="输出图片分辨率(DPI)"),
|
||||||
|
rotation: int = Form(0, description="强制旋转角度 (0/90/-90/180)"),
|
||||||
|
current_username: str = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""PLT 裁片处理接口(优化版)"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. 解析参数
|
||||||
|
try:
|
||||||
|
size_labels_list = json.loads(size_labels)
|
||||||
|
except:
|
||||||
|
raise HTTPException(status_code=400, detail="size_labels 格式错误")
|
||||||
|
|
||||||
|
size_num = len(size_labels_list)
|
||||||
|
scale_factor = dpi / 1016
|
||||||
|
plt_to_cm = 2.54 / 1016
|
||||||
|
|
||||||
|
# 2. 读取并解析 PLT 文件
|
||||||
|
print(f"[PLT API] 用户 {current_username} 正在处理: {file.filename}")
|
||||||
|
file_content = (await file.read()).decode('utf-8', errors='ignore')
|
||||||
|
output = parse_plt_file(file_content)
|
||||||
|
|
||||||
|
# 3. 获取尺码聚类
|
||||||
|
clusters = output.get_single_size_info(size_num=size_num)
|
||||||
|
|
||||||
|
# 4. 获取尺码间匹配关系(优化版)
|
||||||
|
match_result = get_size_matching(clusters, size_num)
|
||||||
|
|
||||||
|
# 5. 构建索引映射
|
||||||
|
all_nodes = {node["index"]: node for node in output.nodes}
|
||||||
|
|
||||||
|
# 6. 计算全局边界
|
||||||
|
from shapely.geometry import MultiPolygon as ShapelyMultiPolygon
|
||||||
|
all_polygons = [node["data"] for node in output.nodes]
|
||||||
|
global_bounds = ShapelyMultiPolygon(all_polygons).bounds
|
||||||
|
|
||||||
|
# 7. 并行生成图片和坐标数据
|
||||||
|
print(f"[PLT API] 开始并行生成图片...")
|
||||||
|
|
||||||
|
# 准备所有任务参数
|
||||||
|
tasks = []
|
||||||
|
for group_id, (base_index, matched_indices) in enumerate(match_result.items(), start=1):
|
||||||
|
for size_idx, piece_index in enumerate(matched_indices):
|
||||||
|
size_label = size_labels_list[size_idx]
|
||||||
|
node = all_nodes[piece_index]
|
||||||
|
tasks.append((
|
||||||
|
output, node, scale_factor, rotation, dpi,
|
||||||
|
size_label, group_id, global_bounds, plt_to_cm
|
||||||
|
))
|
||||||
|
|
||||||
|
# 使用线程池并行处理(CPU 核心数)
|
||||||
|
max_workers = min(os.cpu_count() or 4, 8) # 最多8线程
|
||||||
|
results = []
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||||
|
future_to_task = {executor.submit(generate_single_piece_image, task): task for task in tasks}
|
||||||
|
for future in as_completed(future_to_task):
|
||||||
|
result = future.result()
|
||||||
|
if result["success"]:
|
||||||
|
results.append(result)
|
||||||
|
|
||||||
|
# 上传图片到七牛云(如果启用)
|
||||||
|
image_urls = {} # key: (group_id, size_label) -> url
|
||||||
|
if qiniu_storage.enabled:
|
||||||
|
# 获取用户ID用于文件隔离
|
||||||
|
from app.models.user import User
|
||||||
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
|
user_id = user.id if user else None
|
||||||
|
|
||||||
|
print(f"[PLT API] 正在上传图片到七牛云,共 {len(results)} 张,用户: {current_username}(id={user_id})...")
|
||||||
|
upload_tasks = []
|
||||||
|
for r in results:
|
||||||
|
base64_data = r["image_base64"]
|
||||||
|
name = f"g{r['group_id']}_{r['size_label']}"
|
||||||
|
upload_tasks.append((base64_data, name))
|
||||||
|
|
||||||
|
# 上传到 plt/{日期}/u{用户ID}/ 目录
|
||||||
|
upload_results = qiniu_storage.upload_batch(upload_tasks, key_prefix="plt", user_id=user_id)
|
||||||
|
|
||||||
|
success_count = 0
|
||||||
|
for i, (success, url_or_base64, name) in enumerate(upload_results):
|
||||||
|
if success:
|
||||||
|
r = results[i]
|
||||||
|
image_urls[(r["group_id"], r["size_label"])] = url_or_base64
|
||||||
|
success_count += 1
|
||||||
|
|
||||||
|
print(f"[PLT API] 七牛云上传完成,成功 {success_count}/{len(results)} 张")
|
||||||
|
|
||||||
|
# 按 group_id 分组
|
||||||
|
groups_dict: Dict[int, List] = {}
|
||||||
|
for r in results:
|
||||||
|
gid = r["group_id"]
|
||||||
|
key = (gid, r["size_label"])
|
||||||
|
|
||||||
|
# 优先使用云存储 URL
|
||||||
|
if key in image_urls:
|
||||||
|
piece = PieceInfo(
|
||||||
|
size=r["size_label"],
|
||||||
|
image_url=image_urls[key],
|
||||||
|
image_base64=None,
|
||||||
|
width_px=r["width_px"],
|
||||||
|
height_px=r["height_px"],
|
||||||
|
center_x_cm=r["center_x_cm"],
|
||||||
|
center_y_cm=r["center_y_cm"],
|
||||||
|
left_cm=r["left_cm"],
|
||||||
|
top_cm=r["top_cm"],
|
||||||
|
width_cm=r["width_cm"],
|
||||||
|
height_cm=r["height_cm"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
piece = PieceInfo(
|
||||||
|
size=r["size_label"],
|
||||||
|
image_base64=r["image_base64"],
|
||||||
|
image_url=None,
|
||||||
|
width_px=r["width_px"],
|
||||||
|
height_px=r["height_px"],
|
||||||
|
center_x_cm=r["center_x_cm"],
|
||||||
|
center_y_cm=r["center_y_cm"],
|
||||||
|
left_cm=r["left_cm"],
|
||||||
|
top_cm=r["top_cm"],
|
||||||
|
width_cm=r["width_cm"],
|
||||||
|
height_cm=r["height_cm"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if gid not in groups_dict:
|
||||||
|
groups_dict[gid] = []
|
||||||
|
groups_dict[gid].append(piece)
|
||||||
|
|
||||||
|
# 构建 groups 列表
|
||||||
|
groups_list = []
|
||||||
|
for gid, pieces in groups_dict.items():
|
||||||
|
# 按尺码排序
|
||||||
|
sorted_pieces = sorted(pieces, key=lambda p: size_labels_list.index(p.size) if p.size in size_labels_list else 999)
|
||||||
|
# 计算该组的面积(用第一个裁片的面积作为代表)
|
||||||
|
area = sorted_pieces[0].width_cm * sorted_pieces[0].height_cm if sorted_pieces else 0
|
||||||
|
groups_list.append((gid, sorted_pieces, area))
|
||||||
|
|
||||||
|
# 按面积从大到小排序,重新分配 group_id
|
||||||
|
groups_list.sort(key=lambda x: -x[2]) # 面积降序
|
||||||
|
groups = [
|
||||||
|
GroupInfo(group_id=new_id, pieces=pieces)
|
||||||
|
for new_id, (_, pieces, _) in enumerate(groups_list, start=1)
|
||||||
|
]
|
||||||
|
|
||||||
|
print(f"[PLT API] 图片生成完成,共 {len(results)} 张")
|
||||||
|
|
||||||
|
# 8. 处理双文件匹配(可选)
|
||||||
|
match_analysis = None
|
||||||
|
if rotated_file:
|
||||||
|
print(f"[PLT API] 双文件匹配: {file.filename} vs {rotated_file.filename}")
|
||||||
|
rotated_content = (await rotated_file.read()).decode('utf-8', errors='ignore')
|
||||||
|
rotated_output = parse_plt_file(rotated_content)
|
||||||
|
rotated_clusters = rotated_output.get_single_size_info(size_num=size_num)
|
||||||
|
|
||||||
|
all_matches = get_match_result_between_files(clusters, rotated_clusters)
|
||||||
|
|
||||||
|
match_analysis = [
|
||||||
|
SizeMatchInfo(size=size_labels_list[i], matches=[MatchInfo(**m) for m in matches])
|
||||||
|
for i, matches in enumerate(all_matches)
|
||||||
|
]
|
||||||
|
|
||||||
|
# 9. 返回结果
|
||||||
|
print(f"[PLT API] 处理完成,共 {len(groups)} 组裁片")
|
||||||
|
return ProcessPltResponse(
|
||||||
|
success=True,
|
||||||
|
total_groups=len(groups),
|
||||||
|
groups=groups,
|
||||||
|
match_analysis=match_analysis
|
||||||
|
)
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[PLT API] 处理失败: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
raise HTTPException(status_code=500, detail=f"处理失败: {str(e)}")
|
||||||
@@ -3,10 +3,12 @@
|
|||||||
记录和分析用户操作
|
记录和分析用户操作
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException, Depends, Header
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from app.core.security import get_current_user
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -14,28 +16,28 @@ router = APIRouter()
|
|||||||
action_logs = []
|
action_logs = []
|
||||||
|
|
||||||
class ActionLog(BaseModel):
|
class ActionLog(BaseModel):
|
||||||
username: str
|
|
||||||
device_id: str
|
|
||||||
action: str
|
action: str
|
||||||
details: Optional[Any] = None
|
details: Optional[Any] = None
|
||||||
timestamp: int
|
timestamp: int
|
||||||
session_id: str
|
session_id: str
|
||||||
|
device_id: Optional[str] = None
|
||||||
|
|
||||||
class ActionLogResponse(BaseModel):
|
class ActionLogResponse(BaseModel):
|
||||||
success: bool
|
success: bool
|
||||||
message: str
|
message: str
|
||||||
|
|
||||||
@router.post("/log", response_model=ActionLogResponse)
|
@router.post("/log", response_model=ActionLogResponse)
|
||||||
async def log_action(log: ActionLog):
|
async def log_action(log: ActionLog, current_username: str = Depends(get_current_user)):
|
||||||
"""
|
"""记录用户行为(仅记录当前登录用户的操作)"""
|
||||||
记录用户行为
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
# 添加服务器时间
|
|
||||||
log_entry = {
|
log_entry = {
|
||||||
**log.dict(),
|
"username": current_username,
|
||||||
|
"action": log.action,
|
||||||
|
"details": log.details,
|
||||||
|
"timestamp": log.timestamp,
|
||||||
|
"session_id": log.session_id,
|
||||||
|
"device_id": log.device_id,
|
||||||
"server_time": datetime.now().isoformat(),
|
"server_time": datetime.now().isoformat(),
|
||||||
"ip": "unknown" # 可以从请求中获取
|
|
||||||
}
|
}
|
||||||
|
|
||||||
action_logs.append(log_entry)
|
action_logs.append(log_entry)
|
||||||
@@ -44,40 +46,37 @@ async def log_action(log: ActionLog):
|
|||||||
if len(action_logs) > 10000:
|
if len(action_logs) > 10000:
|
||||||
action_logs.pop(0)
|
action_logs.pop(0)
|
||||||
|
|
||||||
# 可以在这里添加异常检测逻辑
|
|
||||||
# 例如:检测同一用户短时间内的大量操作
|
|
||||||
|
|
||||||
return ActionLogResponse(success=True, message="已记录")
|
return ActionLogResponse(success=True, message="已记录")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return ActionLogResponse(success=False, message=str(e))
|
return ActionLogResponse(success=False, message=str(e))
|
||||||
|
|
||||||
@router.get("/stats/{username}")
|
@router.get("/stats/me")
|
||||||
async def get_user_stats(username: str):
|
async def get_my_stats(current_username: str = Depends(get_current_user)):
|
||||||
"""
|
"""获取当前用户的统计信息"""
|
||||||
获取用户统计信息
|
user_logs = [log for log in action_logs if log.get("username") == current_username]
|
||||||
"""
|
|
||||||
user_logs = [log for log in action_logs if log.get("username") == username]
|
|
||||||
|
|
||||||
# 统计各操作类型的次数
|
action_counts: dict[str, int] = {}
|
||||||
action_counts = {}
|
|
||||||
for log in user_logs:
|
for log in user_logs:
|
||||||
action = log.get("action", "unknown")
|
action = log.get("action", "unknown")
|
||||||
action_counts[action] = action_counts.get(action, 0) + 1
|
action_counts[action] = action_counts.get(action, 0) + 1
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"username": username,
|
"username": current_username,
|
||||||
"total_actions": len(user_logs),
|
"total_actions": len(user_logs),
|
||||||
"action_counts": action_counts,
|
"action_counts": action_counts,
|
||||||
"recent_actions": user_logs[-10:] # 最近 10 条
|
"recent_actions": user_logs[-10:]
|
||||||
}
|
}
|
||||||
|
|
||||||
@router.get("/recent")
|
@router.get("/recent")
|
||||||
async def get_recent_logs(limit: int = 100):
|
async def get_recent_logs(
|
||||||
"""
|
limit: int = 100,
|
||||||
获取最近的操作日志(管理员用)
|
admin_token: Optional[str] = Header(None, alias="X-Admin-Token")
|
||||||
"""
|
):
|
||||||
|
"""获取最近的操作日志(仅管理员)"""
|
||||||
|
if admin_token != settings.ADMIN_TOKEN:
|
||||||
|
raise HTTPException(status_code=403, detail="需要管理员权限")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"total": len(action_logs),
|
"total": len(action_logs),
|
||||||
"logs": action_logs[-limit:]
|
"logs": action_logs[-limit:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,13 +55,14 @@ async def reset_password(body: ResetPasswordRequest, db: Session = Depends(get_d
|
|||||||
return auth_service.reset_password(db, body.token, body.new_password, body.email)
|
return auth_service.reset_password(db, body.token, body.new_password, body.email)
|
||||||
|
|
||||||
@router.post("/logout")
|
@router.post("/logout")
|
||||||
async def logout(body: UserLogout, db: Session = Depends(get_db)):
|
async def logout(body: UserLogout, db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
# 登出接口:将指定设备会话置为非活跃
|
# 登出接口:只允许登出自己的会话
|
||||||
return auth_service.logout(db, body.username, body.device_id)
|
return auth_service.logout(db, current_username, body.device_id)
|
||||||
|
|
||||||
@router.get("/online-time/{username}")
|
@router.get("/online-time")
|
||||||
async def get_online_time(username: str, db: Session = Depends(get_db)):
|
async def get_online_time(db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
# 在线时长统计:累计历史会话的时长(秒),以及当前活跃会话的实时时长(秒)
|
# 在线时长统计:只查自己的数据
|
||||||
|
username = current_username
|
||||||
user = db.query(User).filter(User.username == username).first()
|
user = db.query(User).filter(User.username == username).first()
|
||||||
if not user:
|
if not user:
|
||||||
return {"username": username, "total_seconds": 0, "active_seconds": 0}
|
return {"username": username, "total_seconds": 0, "active_seconds": 0}
|
||||||
@@ -104,8 +105,8 @@ async def get_online_time(username: str, db: Session = Depends(get_db)):
|
|||||||
return {"username": username, "total_seconds": total_seconds, "active_seconds": active_seconds}
|
return {"username": username, "total_seconds": total_seconds, "active_seconds": active_seconds}
|
||||||
|
|
||||||
@router.post("/heartbeat")
|
@router.post("/heartbeat")
|
||||||
async def heartbeat(body: UserHeartbeat, db: Session = Depends(get_db)):
|
async def heartbeat(body: UserHeartbeat, db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
# 心跳接口:更新会话的最近在线时间
|
# 心跳接口:只更新自己的会话
|
||||||
return auth_service.heartbeat(db, body.username, body.device_id)
|
return auth_service.heartbeat(db, current_username, body.device_id)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,21 +12,17 @@ from sqlalchemy import func
|
|||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.models.business import CheckInConfig, VipConfig, CheckInRecord, PointsHistory
|
from app.models.business import CheckInConfig, VipConfig, CheckInRecord, PointsHistory
|
||||||
|
from app.core.security import get_current_user
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
# ==================== 数据模型 ====================
|
|
||||||
|
|
||||||
class CheckInRequest(BaseModel):
|
|
||||||
username: str
|
|
||||||
|
|
||||||
# ==================== 签到功能 ====================
|
# ==================== 签到功能 ====================
|
||||||
|
|
||||||
@router.post("/checkin/daily")
|
@router.post("/checkin/daily")
|
||||||
async def daily_checkin(data: CheckInRequest, db: Session = Depends(get_db)):
|
async def daily_checkin(db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
"""每日签到"""
|
"""每日签到"""
|
||||||
# 1. 获取用户信息
|
# 1. 获取用户信息
|
||||||
user = db.query(User).filter(User.username == data.username).first()
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
@@ -85,7 +81,7 @@ async def daily_checkin(data: CheckInRequest, db: Session = Depends(get_db)):
|
|||||||
# 7. 记录签到记录
|
# 7. 记录签到记录
|
||||||
checkin_record = CheckInRecord(
|
checkin_record = CheckInRecord(
|
||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
username=data.username,
|
username=current_username,
|
||||||
check_in_date=today,
|
check_in_date=today,
|
||||||
points_earned=points_earned,
|
points_earned=points_earned,
|
||||||
consecutive_days=consecutive_days,
|
consecutive_days=consecutive_days,
|
||||||
@@ -96,7 +92,7 @@ async def daily_checkin(data: CheckInRequest, db: Session = Depends(get_db)):
|
|||||||
# 8. 记录积分历史
|
# 8. 记录积分历史
|
||||||
points_history = PointsHistory(
|
points_history = PointsHistory(
|
||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
username=data.username,
|
username=current_username,
|
||||||
type='checkin',
|
type='checkin',
|
||||||
amount=points_earned,
|
amount=points_earned,
|
||||||
balance=new_balance,
|
balance=new_balance,
|
||||||
@@ -145,9 +141,9 @@ async def get_checkin_config(db: Session = Depends(get_db)):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@router.get("/checkin/status")
|
@router.get("/checkin/status")
|
||||||
async def get_checkin_status(username: str, db: Session = Depends(get_db)):
|
async def get_checkin_status(db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
"""获取签到状态"""
|
"""获取签到状态"""
|
||||||
user = db.query(User).filter(User.username == username).first()
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
@@ -160,14 +156,15 @@ async def get_checkin_status(username: str, db: Session = Depends(get_db)):
|
|||||||
"today_checked": today_checked,
|
"today_checked": today_checked,
|
||||||
"consecutive_days": user.consecutive_check_in if user.consecutive_check_in else 0,
|
"consecutive_days": user.consecutive_check_in if user.consecutive_check_in else 0,
|
||||||
"total_days": user.total_check_in_days if user.total_check_in_days else 0,
|
"total_days": user.total_check_in_days if user.total_check_in_days else 0,
|
||||||
|
"total_points": user.points if user.points else 0,
|
||||||
"last_check_in_date": user.last_check_in_date.isoformat() if user.last_check_in_date else None
|
"last_check_in_date": user.last_check_in_date.isoformat() if user.last_check_in_date else None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@router.get("/checkin/calendar/{year}/{month}")
|
@router.get("/checkin/calendar/{year}/{month}")
|
||||||
async def get_checkin_calendar(username: str, year: int, month: int, db: Session = Depends(get_db)):
|
async def get_checkin_calendar(year: int, month: int, db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
"""获取签到日历"""
|
"""获取签到日历"""
|
||||||
user = db.query(User).filter(User.username == username).first()
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
@@ -199,9 +196,9 @@ async def get_checkin_calendar(username: str, year: int, month: int, db: Session
|
|||||||
}
|
}
|
||||||
|
|
||||||
@router.get("/checkin/history")
|
@router.get("/checkin/history")
|
||||||
async def get_checkin_history(username: str, page: int = 1, limit: int = 10, db: Session = Depends(get_db)):
|
async def get_checkin_history(page: int = 1, limit: int = 10, db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
"""签到记录"""
|
"""签到记录"""
|
||||||
user = db.query(User).filter(User.username == username).first()
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
@@ -232,23 +229,3 @@ async def get_checkin_history(username: str, page: int = 1, limit: int = 10, db:
|
|||||||
"records": result_records
|
"records": result_records
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@router.get("/checkin/config")
|
|
||||||
async def get_checkin_config(db: Session = Depends(get_db)):
|
|
||||||
"""获取签到奖励规则 (公开接口)"""
|
|
||||||
configs = db.query(CheckInConfig).filter(CheckInConfig.enabled == True).order_by(CheckInConfig.consecutive_days.asc()).all()
|
|
||||||
|
|
||||||
data = []
|
|
||||||
for c in configs:
|
|
||||||
data.append({
|
|
||||||
"consecutive_days": c.consecutive_days,
|
|
||||||
"base_points": c.base_points,
|
|
||||||
"bonus_points": c.bonus_points,
|
|
||||||
"total_points": c.total_points
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
"code": 200,
|
|
||||||
"data": data
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,20 +11,19 @@ from sqlalchemy.orm import Session
|
|||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.models.business import FeatureConfig, VipConfig, PointsHistory
|
from app.models.business import FeatureConfig, VipConfig, PointsHistory
|
||||||
|
from app.core.security import get_current_user
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
# ==================== 数据模型 ====================
|
# ==================== 数据模型 ====================
|
||||||
|
|
||||||
class UseFeatureRequest(BaseModel):
|
class UseFeatureRequest(BaseModel):
|
||||||
username: str
|
|
||||||
feature_key: str
|
feature_key: str
|
||||||
device_id: str
|
|
||||||
|
|
||||||
# ==================== 通用功能使用 ====================
|
# ==================== 通用功能使用 ====================
|
||||||
|
|
||||||
@router.post("/feature/use")
|
@router.post("/feature/use")
|
||||||
async def use_feature(data: UseFeatureRequest, db: Session = Depends(get_db)):
|
async def use_feature(data: UseFeatureRequest, db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
"""
|
"""
|
||||||
通用功能使用接口
|
通用功能使用接口
|
||||||
逻辑:
|
逻辑:
|
||||||
@@ -38,7 +37,7 @@ async def use_feature(data: UseFeatureRequest, db: Session = Depends(get_db)):
|
|||||||
raise HTTPException(status_code=400, detail="功能不存在或已禁用")
|
raise HTTPException(status_code=400, detail="功能不存在或已禁用")
|
||||||
|
|
||||||
# 2. 获取用户信息
|
# 2. 获取用户信息
|
||||||
user = db.query(User).filter(User.username == data.username).first()
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
@@ -100,7 +99,7 @@ async def use_feature(data: UseFeatureRequest, db: Session = Depends(get_db)):
|
|||||||
# 记录积分历史
|
# 记录积分历史
|
||||||
points_history = PointsHistory(
|
points_history = PointsHistory(
|
||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
username=data.username,
|
username=current_username,
|
||||||
type='consume',
|
type='consume',
|
||||||
amount=-points_cost,
|
amount=-points_cost,
|
||||||
balance=new_balance,
|
balance=new_balance,
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
"""
|
|
||||||
服务器端计算 Demo - 简单数学计算
|
|
||||||
演示:前端获取图层名称 → 后端计算数学表达式 → 返回结果
|
|
||||||
"""
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Header, HTTPException
|
|
||||||
from pydantic import BaseModel
|
|
||||||
import re
|
|
||||||
import logging
|
|
||||||
from datetime import datetime
|
|
||||||
from typing import Optional
|
|
||||||
from app.core.api_keys import validate_api_key, get_key_info
|
|
||||||
|
|
||||||
# 配置日志
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s [%(levelname)s] %(message)s',
|
|
||||||
datefmt='%Y-%m-%d %H:%M:%S'
|
|
||||||
)
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
router = APIRouter()
|
|
||||||
|
|
||||||
class CalculateRequest(BaseModel):
|
|
||||||
"""计算请求"""
|
|
||||||
expression: str # 数学表达式,如 "87-98"
|
|
||||||
|
|
||||||
class CalculateResult(BaseModel):
|
|
||||||
"""计算结果"""
|
|
||||||
success: bool
|
|
||||||
expression: str
|
|
||||||
result: float = None
|
|
||||||
message: str
|
|
||||||
|
|
||||||
@router.post("/calculate", response_model=CalculateResult)
|
|
||||||
async def calculate_expression(
|
|
||||||
request: CalculateRequest,
|
|
||||||
x_api_key: Optional[str] = Header(None) # 可选的 API Key 验证
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
🔒 服务器端数学计算(核心算法)
|
|
||||||
客户端只能拿到计算结果,看不到算法
|
|
||||||
|
|
||||||
示例:前端发送 "87-98" → 后端计算 → 返回 -11
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ==================== 📝 日志:打印请求 ====================
|
|
||||||
logger.info("="*60)
|
|
||||||
logger.info("📥 收到计算请求")
|
|
||||||
logger.info(f" 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
||||||
logger.info(f" 表达式: {request.expression}")
|
|
||||||
logger.info(f" API Key: {x_api_key if x_api_key else '未提供'}")
|
|
||||||
logger.info("="*60)
|
|
||||||
|
|
||||||
# ==================== 🔐 API Key 验证 ====================
|
|
||||||
if not validate_api_key(x_api_key):
|
|
||||||
logger.warning(f"❌ API Key 验证失败: {x_api_key}")
|
|
||||||
raise HTTPException(status_code=403, detail="无效的 API Key")
|
|
||||||
|
|
||||||
# 获取 Key 信息
|
|
||||||
key_info = get_key_info(x_api_key)
|
|
||||||
logger.info(f"✅ API Key 验证通过 | 名称: {key_info['name']} | 权限: {key_info['permissions']}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 🔒 核心算法在这里(客户端看不到)
|
|
||||||
# 可以是复杂的数学模型、AI 推理等
|
|
||||||
|
|
||||||
expression = request.expression.strip()
|
|
||||||
|
|
||||||
# ==================== 🛡️ 安全检查 ====================
|
|
||||||
logger.info(f"🛡️ 安全检查: 验证表达式格式...")
|
|
||||||
|
|
||||||
# 只允许数字和基本运算符
|
|
||||||
if not re.match(r'^[\d\s\+\-\*\/\(\)\.]+$', expression):
|
|
||||||
logger.warning(f"❌ 表达式包含非法字符: {expression}")
|
|
||||||
return CalculateResult(
|
|
||||||
success=False,
|
|
||||||
expression=expression,
|
|
||||||
message="只支持基本数学运算(+、-、*、/)"
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info("✅ 表达式格式验证通过")
|
|
||||||
|
|
||||||
# ==================== 🔒 核心算法执行 ====================
|
|
||||||
logger.info("🔒 开始执行核心算法...")
|
|
||||||
|
|
||||||
# 这里可以放你的核心算法
|
|
||||||
# 示例:简单计算
|
|
||||||
result = eval(expression)
|
|
||||||
|
|
||||||
logger.info(f"✅ 计算完成: {expression} = {result}")
|
|
||||||
|
|
||||||
# ==================== 📤 日志:打印输出 ====================
|
|
||||||
response = CalculateResult(
|
|
||||||
success=True,
|
|
||||||
expression=expression,
|
|
||||||
result=float(result),
|
|
||||||
message=f"计算成功: {expression} = {result}"
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info("="*60)
|
|
||||||
logger.info("📤 返回计算结果")
|
|
||||||
logger.info(f" 成功: {response.success}")
|
|
||||||
logger.info(f" 表达式: {response.expression}")
|
|
||||||
logger.info(f" 结果: {response.result}")
|
|
||||||
logger.info(f" 消息: {response.message}")
|
|
||||||
logger.info("="*60)
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"❌ 计算失败: {str(e)}")
|
|
||||||
logger.error("="*60)
|
|
||||||
|
|
||||||
return CalculateResult(
|
|
||||||
success=False,
|
|
||||||
expression=request.expression,
|
|
||||||
message=f"计算失败: {str(e)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@@ -7,7 +7,6 @@ from fastapi import APIRouter, HTTPException, Depends
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any
|
||||||
from app.core.security import get_current_user
|
from app.core.security import get_current_user
|
||||||
from app.db import get_db_session
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@@ -74,14 +73,14 @@ class JSXExecuteResponse(BaseModel):
|
|||||||
@router.post("/execute", response_model=JSXExecuteResponse)
|
@router.post("/execute", response_model=JSXExecuteResponse)
|
||||||
async def execute_jsx(
|
async def execute_jsx(
|
||||||
request: JSXExecuteRequest,
|
request: JSXExecuteRequest,
|
||||||
current_user: dict = Depends(get_current_user)
|
current_user: str = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
服务器端生成 JSX 代码
|
服务器端生成 JSX 代码
|
||||||
客户端只能通过 API 获取,无法直接看到核心逻辑
|
客户端只能通过 API 获取,无法直接看到核心逻辑
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
username = current_user.get("username")
|
username = current_user # get_current_user 返回用户名字符串
|
||||||
|
|
||||||
# 1. 验证用户和设备
|
# 1. 验证用户和设备
|
||||||
# ... (从数据库检查用户是否有权限、设备是否绑定)
|
# ... (从数据库检查用户是否有权限、设备是否绑定)
|
||||||
@@ -118,7 +117,7 @@ async def execute_jsx(
|
|||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
@router.get("/templates")
|
@router.get("/templates")
|
||||||
async def list_templates(current_user: dict = Depends(get_current_user)):
|
async def list_templates(current_user: str = Depends(get_current_user)):
|
||||||
"""
|
"""
|
||||||
列出可用的 JSX 模板(不返回具体代码)
|
列出可用的 JSX 模板(不返回具体代码)
|
||||||
"""
|
"""
|
||||||
|
|||||||
340
Server/app/api/v1/logs.py
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
日志和历史记录API
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from sqlalchemy.orm import Session, joinedload
|
||||||
|
from sqlalchemy import desc
|
||||||
|
|
||||||
|
from app.db import get_db
|
||||||
|
from app.core.security import get_current_user
|
||||||
|
from app.models.logs import PltProcessRecord, UserActionLog, PltPiece
|
||||||
|
from app.models.user import User
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 数据模型 ====================
|
||||||
|
|
||||||
|
class ActionLogCreate(BaseModel):
|
||||||
|
"""创建操作日志"""
|
||||||
|
session_id: Optional[str] = None
|
||||||
|
action_type: str
|
||||||
|
action_params: Optional[dict] = None
|
||||||
|
page: Optional[str] = None
|
||||||
|
result: str = "success"
|
||||||
|
error_message: Optional[str] = None
|
||||||
|
duration_ms: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ActionLogResponse(BaseModel):
|
||||||
|
"""操作日志响应"""
|
||||||
|
id: int
|
||||||
|
action_type: str
|
||||||
|
action_params: Optional[dict]
|
||||||
|
page: Optional[str]
|
||||||
|
result: str
|
||||||
|
error_message: Optional[str]
|
||||||
|
duration_ms: Optional[int]
|
||||||
|
created_at: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class PltPieceCreate(BaseModel):
|
||||||
|
"""裁片信息"""
|
||||||
|
group_id: int
|
||||||
|
size: str
|
||||||
|
image_url: Optional[str] = None
|
||||||
|
width_px: int = 0
|
||||||
|
height_px: int = 0
|
||||||
|
width_cm: float = 0
|
||||||
|
height_cm: float = 0
|
||||||
|
left_cm: float = 0
|
||||||
|
top_cm: float = 0
|
||||||
|
center_x_cm: float = 0
|
||||||
|
center_y_cm: float = 0
|
||||||
|
|
||||||
|
|
||||||
|
class PltPieceResponse(BaseModel):
|
||||||
|
"""裁片响应"""
|
||||||
|
id: int
|
||||||
|
group_id: int
|
||||||
|
size: str
|
||||||
|
image_url: Optional[str]
|
||||||
|
width_px: int
|
||||||
|
height_px: int
|
||||||
|
width_cm: float
|
||||||
|
height_cm: float
|
||||||
|
left_cm: float
|
||||||
|
top_cm: float
|
||||||
|
center_x_cm: float
|
||||||
|
center_y_cm: float
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class PltRecordCreate(BaseModel):
|
||||||
|
"""创建PLT处理记录"""
|
||||||
|
filename: Optional[str] = None
|
||||||
|
file_size: Optional[int] = None
|
||||||
|
rotated_filename: Optional[str] = None
|
||||||
|
size_labels: Optional[List[str]] = None
|
||||||
|
dpi: int = 150
|
||||||
|
rotation: int = 0
|
||||||
|
total_groups: int = 0
|
||||||
|
total_pieces: int = 0
|
||||||
|
process_time_ms: Optional[int] = None
|
||||||
|
status: str = "success"
|
||||||
|
error_message: Optional[str] = None
|
||||||
|
pieces: Optional[List[PltPieceCreate]] = None # 裁片详情
|
||||||
|
|
||||||
|
|
||||||
|
class PltRecordResponse(BaseModel):
|
||||||
|
"""PLT处理记录响应"""
|
||||||
|
id: int
|
||||||
|
filename: Optional[str]
|
||||||
|
file_size: Optional[int]
|
||||||
|
rotated_filename: Optional[str]
|
||||||
|
size_labels: Optional[List[str]]
|
||||||
|
dpi: int
|
||||||
|
rotation: int
|
||||||
|
total_groups: int
|
||||||
|
total_pieces: int
|
||||||
|
process_time_ms: Optional[int]
|
||||||
|
status: str
|
||||||
|
created_at: datetime
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class PltRecordDetailResponse(PltRecordResponse):
|
||||||
|
"""PLT处理记录详情响应(包含裁片)"""
|
||||||
|
pieces: List[PltPieceResponse] = []
|
||||||
|
|
||||||
|
|
||||||
|
class SessionLogsResponse(BaseModel):
|
||||||
|
"""会话日志响应(给AI用)"""
|
||||||
|
user_id: int
|
||||||
|
username: str
|
||||||
|
current_page: Optional[str]
|
||||||
|
recent_actions: List[ActionLogResponse]
|
||||||
|
recent_plt_records: List[PltRecordResponse]
|
||||||
|
error_count: int
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== API端点 ====================
|
||||||
|
|
||||||
|
@router.post("/logs/action", response_model=ActionLogResponse)
|
||||||
|
async def create_action_log(
|
||||||
|
log_data: ActionLogCreate,
|
||||||
|
current_username: str = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""记录用户操作日志"""
|
||||||
|
# 获取用户ID
|
||||||
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
|
# 创建日志
|
||||||
|
log = UserActionLog(
|
||||||
|
user_id=user.id,
|
||||||
|
session_id=log_data.session_id,
|
||||||
|
action_type=log_data.action_type,
|
||||||
|
action_params=log_data.action_params,
|
||||||
|
page=log_data.page,
|
||||||
|
result=log_data.result,
|
||||||
|
error_message=log_data.error_message,
|
||||||
|
duration_ms=log_data.duration_ms
|
||||||
|
)
|
||||||
|
db.add(log)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(log)
|
||||||
|
|
||||||
|
return log
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/plt/record", response_model=PltRecordResponse)
|
||||||
|
async def create_plt_record(
|
||||||
|
record_data: PltRecordCreate,
|
||||||
|
current_username: str = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""记录PLT处理记录(包含裁片详情)"""
|
||||||
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
|
record = PltProcessRecord(
|
||||||
|
user_id=user.id,
|
||||||
|
filename=record_data.filename,
|
||||||
|
file_size=record_data.file_size,
|
||||||
|
rotated_filename=record_data.rotated_filename,
|
||||||
|
size_labels=record_data.size_labels,
|
||||||
|
dpi=record_data.dpi,
|
||||||
|
rotation=record_data.rotation,
|
||||||
|
total_groups=record_data.total_groups,
|
||||||
|
total_pieces=record_data.total_pieces,
|
||||||
|
process_time_ms=record_data.process_time_ms,
|
||||||
|
status=record_data.status,
|
||||||
|
error_message=record_data.error_message
|
||||||
|
)
|
||||||
|
db.add(record)
|
||||||
|
db.flush() # 获取 record.id
|
||||||
|
|
||||||
|
# 保存裁片详情
|
||||||
|
if record_data.pieces:
|
||||||
|
for piece_data in record_data.pieces:
|
||||||
|
piece = PltPiece(
|
||||||
|
record_id=record.id,
|
||||||
|
group_id=piece_data.group_id,
|
||||||
|
size=piece_data.size,
|
||||||
|
image_url=piece_data.image_url,
|
||||||
|
width_px=piece_data.width_px,
|
||||||
|
height_px=piece_data.height_px,
|
||||||
|
width_cm=piece_data.width_cm,
|
||||||
|
height_cm=piece_data.height_cm,
|
||||||
|
left_cm=piece_data.left_cm,
|
||||||
|
top_cm=piece_data.top_cm,
|
||||||
|
center_x_cm=piece_data.center_x_cm,
|
||||||
|
center_y_cm=piece_data.center_y_cm
|
||||||
|
)
|
||||||
|
db.add(piece)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
db.refresh(record)
|
||||||
|
|
||||||
|
return record
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/plt/history/{record_id}", response_model=PltRecordDetailResponse)
|
||||||
|
async def get_plt_record_detail(
|
||||||
|
record_id: int,
|
||||||
|
current_username: str = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""获取PLT处理记录详情(包含裁片)"""
|
||||||
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
|
record = db.query(PltProcessRecord)\
|
||||||
|
.options(joinedload(PltProcessRecord.pieces))\
|
||||||
|
.filter(PltProcessRecord.id == record_id)\
|
||||||
|
.filter(PltProcessRecord.user_id == user.id)\
|
||||||
|
.first()
|
||||||
|
|
||||||
|
if not record:
|
||||||
|
raise HTTPException(status_code=404, detail="记录不存在")
|
||||||
|
|
||||||
|
return record
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/plt/history", response_model=List[PltRecordResponse])
|
||||||
|
async def get_plt_history(
|
||||||
|
limit: int = Query(20, ge=1, le=100),
|
||||||
|
days: int = Query(7, ge=1, le=30), # 保留天数,默认7天
|
||||||
|
current_username: str = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""获取用户的PLT处理历史(默认保留7天)"""
|
||||||
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
|
# 只返回指定天数内的记录
|
||||||
|
since = datetime.now() - timedelta(days=days)
|
||||||
|
|
||||||
|
records = db.query(PltProcessRecord)\
|
||||||
|
.filter(PltProcessRecord.user_id == user.id)\
|
||||||
|
.filter(PltProcessRecord.created_at >= since)\
|
||||||
|
.order_by(desc(PltProcessRecord.created_at))\
|
||||||
|
.limit(limit)\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/logs/recent", response_model=List[ActionLogResponse])
|
||||||
|
async def get_recent_logs(
|
||||||
|
limit: int = Query(50, ge=1, le=200),
|
||||||
|
action_type: Optional[str] = None,
|
||||||
|
current_username: str = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""获取用户最近的操作日志"""
|
||||||
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
|
query = db.query(UserActionLog).filter(UserActionLog.user_id == user.id)
|
||||||
|
|
||||||
|
if action_type:
|
||||||
|
query = query.filter(UserActionLog.action_type.like(f"{action_type}%"))
|
||||||
|
|
||||||
|
logs = query.order_by(desc(UserActionLog.created_at)).limit(limit).all()
|
||||||
|
|
||||||
|
return logs
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/logs/session", response_model=SessionLogsResponse)
|
||||||
|
async def get_session_context(
|
||||||
|
hours: int = Query(24, ge=1, le=168),
|
||||||
|
current_username: str = Depends(get_current_user),
|
||||||
|
db: Session = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""获取会话上下文(给AI助手用)"""
|
||||||
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
|
since = datetime.now() - timedelta(hours=hours)
|
||||||
|
|
||||||
|
# 最近的操作日志
|
||||||
|
recent_actions = db.query(UserActionLog)\
|
||||||
|
.filter(UserActionLog.user_id == user.id)\
|
||||||
|
.filter(UserActionLog.created_at >= since)\
|
||||||
|
.order_by(desc(UserActionLog.created_at))\
|
||||||
|
.limit(50)\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
# 最近的PLT处理记录
|
||||||
|
recent_plt = db.query(PltProcessRecord)\
|
||||||
|
.filter(PltProcessRecord.user_id == user.id)\
|
||||||
|
.filter(PltProcessRecord.created_at >= since)\
|
||||||
|
.order_by(desc(PltProcessRecord.created_at))\
|
||||||
|
.limit(10)\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
# 错误数量
|
||||||
|
error_count = db.query(UserActionLog)\
|
||||||
|
.filter(UserActionLog.user_id == user.id)\
|
||||||
|
.filter(UserActionLog.created_at >= since)\
|
||||||
|
.filter(UserActionLog.result == "error")\
|
||||||
|
.count()
|
||||||
|
|
||||||
|
# 当前页面(最近一次进入页面的记录)
|
||||||
|
last_page_action = db.query(UserActionLog)\
|
||||||
|
.filter(UserActionLog.user_id == user.id)\
|
||||||
|
.filter(UserActionLog.action_type == "page.enter")\
|
||||||
|
.order_by(desc(UserActionLog.created_at))\
|
||||||
|
.first()
|
||||||
|
|
||||||
|
current_page = last_page_action.action_params.get("page") if last_page_action and last_page_action.action_params else None
|
||||||
|
|
||||||
|
return SessionLogsResponse(
|
||||||
|
user_id=user.id,
|
||||||
|
username=current_username,
|
||||||
|
current_page=current_page,
|
||||||
|
recent_actions=recent_actions,
|
||||||
|
recent_plt_records=recent_plt,
|
||||||
|
error_count=error_count
|
||||||
|
)
|
||||||
@@ -8,6 +8,7 @@ from fastapi import APIRouter, Header, HTTPException
|
|||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
import pymysql
|
import pymysql
|
||||||
from app.core.database import get_db_connection
|
from app.core.database import get_db_connection
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@@ -15,8 +16,7 @@ router = APIRouter()
|
|||||||
|
|
||||||
def verify_admin_token(token: str):
|
def verify_admin_token(token: str):
|
||||||
"""验证管理员Token"""
|
"""验证管理员Token"""
|
||||||
expected_token = "admin-secret-token"
|
if token != settings.ADMIN_TOKEN:
|
||||||
if token != expected_token:
|
|
||||||
raise HTTPException(status_code=401, detail="管理员Token无效")
|
raise HTTPException(status_code=401, detail="管理员Token无效")
|
||||||
|
|
||||||
# ==================== 统计功能 ====================
|
# ==================== 统计功能 ====================
|
||||||
|
|||||||
@@ -8,17 +8,16 @@ from fastapi import APIRouter, HTTPException, Depends
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy import func
|
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.models.business import PointsHistory
|
from app.models.business import PointsHistory
|
||||||
|
from app.core.security import get_current_user
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
# ==================== 数据模型 ====================
|
# ==================== 数据模型 ====================
|
||||||
|
|
||||||
class UserProfileUpdate(BaseModel):
|
class UserProfileUpdate(BaseModel):
|
||||||
username: str
|
|
||||||
nickname: Optional[str] = None
|
nickname: Optional[str] = None
|
||||||
avatar: Optional[str] = None
|
avatar: Optional[str] = None
|
||||||
email: Optional[str] = None
|
email: Optional[str] = None
|
||||||
@@ -26,13 +25,12 @@ class UserProfileUpdate(BaseModel):
|
|||||||
# ==================== 用户资料管理 ====================
|
# ==================== 用户资料管理 ====================
|
||||||
|
|
||||||
@router.get("/user/profile")
|
@router.get("/user/profile")
|
||||||
async def get_user_profile(username: str, db: Session = Depends(get_db)):
|
async def get_user_profile(db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
"""获取用户资料"""
|
"""获取用户资料"""
|
||||||
user = db.query(User).filter(User.username == username).first()
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
# 转换为字典以便序列化
|
|
||||||
user_data = {
|
user_data = {
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"nickname": user.nickname,
|
"nickname": user.nickname,
|
||||||
@@ -54,9 +52,9 @@ async def get_user_profile(username: str, db: Session = Depends(get_db)):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@router.put("/user/profile")
|
@router.put("/user/profile")
|
||||||
async def update_user_profile(data: UserProfileUpdate, db: Session = Depends(get_db)):
|
async def update_user_profile(data: UserProfileUpdate, db: Session = Depends(get_db), current_username: str = Depends(get_current_user)):
|
||||||
"""更新用户资料"""
|
"""更新用户资料"""
|
||||||
user = db.query(User).filter(User.username == data.username).first()
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
@@ -78,14 +76,14 @@ async def update_user_profile(data: UserProfileUpdate, db: Session = Depends(get
|
|||||||
|
|
||||||
@router.get("/points/history")
|
@router.get("/points/history")
|
||||||
async def get_points_history(
|
async def get_points_history(
|
||||||
username: str,
|
|
||||||
type: Optional[str] = None,
|
type: Optional[str] = None,
|
||||||
page: int = 1,
|
page: int = 1,
|
||||||
limit: int = 20,
|
limit: int = 20,
|
||||||
db: Session = Depends(get_db)
|
db: Session = Depends(get_db),
|
||||||
|
current_username: str = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""获取积分历史"""
|
"""获取积分历史"""
|
||||||
user = db.query(User).filter(User.username == username).first()
|
user = db.query(User).filter(User.username == current_username).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(status_code=404, detail="用户不存在")
|
raise HTTPException(status_code=404, detail="用户不存在")
|
||||||
|
|
||||||
@@ -117,4 +115,3 @@ async def get_points_history(
|
|||||||
"records": result_records
|
"records": result_records
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,37 @@ class Settings(BaseSettings):
|
|||||||
PROJECT_NAME: str = "DesignerCEP Backend"
|
PROJECT_NAME: str = "DesignerCEP Backend"
|
||||||
API_V1_STR: str = "/api/v1"
|
API_V1_STR: str = "/api/v1"
|
||||||
DATABASE_URL: str = "sqlite:///./designercep.db"
|
DATABASE_URL: str = "sqlite:///./designercep.db"
|
||||||
SECRET_KEY: str = "change-me"
|
SECRET_KEY: str = "" # 必须从 .env 读取
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 10080
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 10080
|
||||||
|
|
||||||
ALLOWED_ORIGINS: str = "*"
|
ALLOWED_ORIGINS: str = "*"
|
||||||
ADMIN_TOKEN: str = "admin-token"
|
ADMIN_TOKEN: str = "" # 必须从 .env 读取
|
||||||
|
|
||||||
# Email Configuration
|
# Email Configuration
|
||||||
SMTP_HOST: str = "smtp.gmail.com"
|
SMTP_HOST: str = "smtp.gmail.com"
|
||||||
SMTP_PORT: int = 587
|
SMTP_PORT: int = 587
|
||||||
SMTP_USER: str = "ly1104803132@gmail.com"
|
SMTP_USER: str = ""
|
||||||
SMTP_PASSWORD: str = "wsfrpnmkojpsqdkk"
|
SMTP_PASSWORD: str = "" # 必须从 .env 读取
|
||||||
EMAILS_FROM_EMAIL: str = "ly1104803132@gmail.com"
|
EMAILS_FROM_EMAIL: str = ""
|
||||||
EMAILS_FROM_NAME: str = "Designer"
|
EMAILS_FROM_NAME: str = "Designer"
|
||||||
|
|
||||||
|
# 七牛云对象存储
|
||||||
|
QINIU_ACCESS_KEY: str = ""
|
||||||
|
QINIU_SECRET_KEY: str = ""
|
||||||
|
QINIU_BUCKET: str = ""
|
||||||
|
QINIU_DOMAIN: str = ""
|
||||||
|
|
||||||
|
# AI 配置 — 通义千问 Qwen(DashScope)
|
||||||
|
AI_API_KEY: str = "" # LLM API Key(OpenAI / DeepSeek 等)
|
||||||
|
AI_BASE_URL: str = "" # LLM API 地址(留空则用 OpenAI 默认)
|
||||||
|
AI_MODEL: str = "qwen3-max-2026-01-23" # 默认模型(通义千问3深度思考)
|
||||||
|
AI_VISION_MODEL: str = "qwen-vl-max-latest" # 视觉分析模型(看图分析成衣)
|
||||||
|
AI_IMAGE_EDIT_MODEL: str = "qwen-image-edit-max-2026-01-16" # 图片编辑/生成模型
|
||||||
|
|
||||||
|
# AI 配置 — Gemini(第三方代理)
|
||||||
|
GEMINI_API_KEY: str = ""
|
||||||
|
GEMINI_BASE_URL: str = "https://api.apiqik.online"
|
||||||
|
|
||||||
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|||||||
154
Server/app/core/qiniu_storage.py
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
七牛云对象存储工具
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional, Tuple
|
||||||
|
from qiniu import Auth, put_data, BucketManager
|
||||||
|
|
||||||
|
from app.core.config import settings
|
||||||
|
|
||||||
|
# ==================== 配置 ====================
|
||||||
|
# 从 settings 读取(通过 pydantic-settings 加载 .env)
|
||||||
|
|
||||||
|
QINIU_ACCESS_KEY = settings.QINIU_ACCESS_KEY
|
||||||
|
QINIU_SECRET_KEY = settings.QINIU_SECRET_KEY
|
||||||
|
QINIU_BUCKET = settings.QINIU_BUCKET
|
||||||
|
QINIU_DOMAIN = settings.QINIU_DOMAIN # 例如: http://cdn.example.com
|
||||||
|
|
||||||
|
# 是否启用七牛云存储
|
||||||
|
QINIU_ENABLED = bool(QINIU_ACCESS_KEY and QINIU_SECRET_KEY and QINIU_BUCKET and QINIU_DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
class QiniuStorage:
|
||||||
|
"""七牛云存储工具类"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.enabled = QINIU_ENABLED
|
||||||
|
if self.enabled:
|
||||||
|
self.auth = Auth(QINIU_ACCESS_KEY, QINIU_SECRET_KEY)
|
||||||
|
self.bucket = QINIU_BUCKET
|
||||||
|
self.domain = QINIU_DOMAIN.rstrip('/')
|
||||||
|
print(f"✅ 七牛云存储已启用,Bucket: {self.bucket}")
|
||||||
|
else:
|
||||||
|
self.auth = None
|
||||||
|
self.bucket = None
|
||||||
|
self.domain = None
|
||||||
|
print("⚠️ 七牛云存储未配置,图片将使用 base64 返回")
|
||||||
|
|
||||||
|
def upload_base64(
|
||||||
|
self,
|
||||||
|
base64_data: str,
|
||||||
|
key_prefix: str = "plt",
|
||||||
|
user_id: Optional[int] = None
|
||||||
|
) -> Tuple[bool, str]:
|
||||||
|
"""
|
||||||
|
上传 base64 图片到七牛云
|
||||||
|
|
||||||
|
Args:
|
||||||
|
base64_data: base64 编码的图片数据(可带或不带 data:image/png;base64, 前缀)
|
||||||
|
key_prefix: 文件名前缀
|
||||||
|
user_id: 用户ID(用于分目录)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(success, url_or_error): 成功返回 URL,失败返回错误信息
|
||||||
|
"""
|
||||||
|
if not self.enabled:
|
||||||
|
return False, "七牛云存储未启用"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 去掉 base64 前缀
|
||||||
|
if ',' in base64_data:
|
||||||
|
base64_data = base64_data.split(',')[1]
|
||||||
|
|
||||||
|
# 解码
|
||||||
|
image_data = base64.b64decode(base64_data)
|
||||||
|
|
||||||
|
# 生成文件名: plt/2024/02/05/u123/时间戳_哈希.png
|
||||||
|
now = datetime.now()
|
||||||
|
date_path = now.strftime("%Y/%m/%d")
|
||||||
|
timestamp = now.strftime("%H%M%S") # 时分秒
|
||||||
|
content_hash = hashlib.md5(image_data).hexdigest()[:8]
|
||||||
|
|
||||||
|
if user_id:
|
||||||
|
key = f"{key_prefix}/{date_path}/u{user_id}/{timestamp}_{content_hash}.png"
|
||||||
|
else:
|
||||||
|
key = f"{key_prefix}/{date_path}/{timestamp}_{content_hash}.png"
|
||||||
|
|
||||||
|
# 生成上传凭证
|
||||||
|
token = self.auth.upload_token(self.bucket, key, 3600)
|
||||||
|
|
||||||
|
# 上传
|
||||||
|
ret, info = put_data(token, key, image_data)
|
||||||
|
|
||||||
|
if info.status_code == 200:
|
||||||
|
url = f"{self.domain}/{key}"
|
||||||
|
return True, url
|
||||||
|
else:
|
||||||
|
print(f"[七牛云] 上传失败: status={info.status_code}, error={info.error}, text={info.text_body}")
|
||||||
|
return False, f"上传失败: {info.error}"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[七牛云] 上传异常: {str(e)}")
|
||||||
|
return False, f"上传异常: {str(e)}"
|
||||||
|
|
||||||
|
def upload_batch(
|
||||||
|
self,
|
||||||
|
images: list, # [(base64_data, name), ...]
|
||||||
|
key_prefix: str = "plt",
|
||||||
|
user_id: Optional[int] = None
|
||||||
|
) -> list:
|
||||||
|
"""
|
||||||
|
批量上传图片
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
[(success, url_or_base64, name), ...]
|
||||||
|
"""
|
||||||
|
results = []
|
||||||
|
success_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
|
||||||
|
for base64_data, name in images:
|
||||||
|
if self.enabled:
|
||||||
|
success, result = self.upload_base64(base64_data, key_prefix, user_id)
|
||||||
|
if success:
|
||||||
|
results.append((True, result, name))
|
||||||
|
success_count += 1
|
||||||
|
else:
|
||||||
|
# 上传失败,回退到 base64
|
||||||
|
print(f"[七牛云] 上传失败 {name}: {result}")
|
||||||
|
results.append((False, base64_data, name))
|
||||||
|
fail_count += 1
|
||||||
|
else:
|
||||||
|
# 未启用,返回原始 base64
|
||||||
|
results.append((False, base64_data, name))
|
||||||
|
|
||||||
|
if fail_count > 0:
|
||||||
|
print(f"[七牛云] 批量上传结果: 成功 {success_count}, 失败 {fail_count}")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def delete(self, key: str) -> bool:
|
||||||
|
"""删除文件"""
|
||||||
|
if not self.enabled:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
bucket_manager = BucketManager(self.auth)
|
||||||
|
ret, info = bucket_manager.delete(self.bucket, key)
|
||||||
|
return info.status_code == 200
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_url(self, key: str) -> str:
|
||||||
|
"""获取文件 URL"""
|
||||||
|
if not self.enabled or not key:
|
||||||
|
return ""
|
||||||
|
return f"{self.domain}/{key}"
|
||||||
|
|
||||||
|
|
||||||
|
# 全局实例
|
||||||
|
qiniu_storage = QiniuStorage()
|
||||||
@@ -34,10 +34,6 @@ def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(
|
|||||||
验证 JWT Token 并返回当前用户名 (sub)
|
验证 JWT Token 并返回当前用户名 (sub)
|
||||||
增加 Session 强校验:检查数据库中 Session 是否活跃
|
增加 Session 强校验:检查数据库中 Session 是否活跃
|
||||||
"""
|
"""
|
||||||
print("="*60)
|
|
||||||
print("[get_current_user] 开始验证 Token")
|
|
||||||
print(f" - Token (前30字符): {token[:30]}...")
|
|
||||||
|
|
||||||
credentials_exception = HTTPException(
|
credentials_exception = HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="无效的认证凭据",
|
detail="无效的认证凭据",
|
||||||
@@ -50,41 +46,24 @@ def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(
|
|||||||
username: str = payload.get("sub")
|
username: str = payload.get("sub")
|
||||||
device_id: str = payload.get("device_id")
|
device_id: str = payload.get("device_id")
|
||||||
|
|
||||||
print(f"[get_current_user] ✓ Token 解析成功")
|
|
||||||
print(f" - username: {username}")
|
|
||||||
print(f" - device_id: {device_id}")
|
|
||||||
print(f" - exp: {payload.get('exp')}")
|
|
||||||
|
|
||||||
if username is None:
|
if username is None:
|
||||||
print("[get_current_user] ✗ username 为空")
|
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
|
|
||||||
except jwt.ExpiredSignatureError:
|
except jwt.ExpiredSignatureError:
|
||||||
print("[get_current_user] ✗ Token 已过期")
|
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
except jwt.InvalidTokenError as e:
|
except jwt.InvalidTokenError:
|
||||||
print(f"[get_current_user] ✗ Token 无效: {e}")
|
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
except jwt.PyJWTError as e:
|
except jwt.PyJWTError:
|
||||||
print(f"[get_current_user] ✗ Token 解析失败: {e}")
|
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
|
|
||||||
# 2. Session 强校验 (如果有 device_id)
|
# 2. Session 强校验 (如果有 device_id)
|
||||||
# 如果 Token 是旧版本没有 device_id,可以选择放行或拒绝。为了安全建议逐步拒绝。
|
|
||||||
# 这里我们假设所有新 Token 都有 device_id
|
|
||||||
if device_id:
|
if device_id:
|
||||||
print(f"[get_current_user] 开始 Session 强校验")
|
|
||||||
# 查询 User ID
|
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
user = db.query(User).filter(User.username == username).first()
|
user = db.query(User).filter(User.username == username).first()
|
||||||
if not user:
|
if not user:
|
||||||
print(f"[get_current_user] ✗ 用户不存在: {username}")
|
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
|
|
||||||
print(f"[get_current_user] ✓ 用户存在: user_id={user.id}")
|
|
||||||
|
|
||||||
# 查询活跃 Session
|
# 查询活跃 Session
|
||||||
print(f"[get_current_user] 查询活跃 Session: user_id={user.id}, device_id={device_id}")
|
|
||||||
session = db.query(UserSession).filter(
|
session = db.query(UserSession).filter(
|
||||||
UserSession.user_id == user.id,
|
UserSession.user_id == user.id,
|
||||||
UserSession.device_id == device_id,
|
UserSession.device_id == device_id,
|
||||||
@@ -92,24 +71,10 @@ def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(
|
|||||||
).first()
|
).first()
|
||||||
|
|
||||||
if not session:
|
if not session:
|
||||||
# Session 不存在或已失效(被踢下线/登出)
|
|
||||||
print(f"[get_current_user] ✗ Session 不存在或已失效")
|
|
||||||
|
|
||||||
# 调试:列出该用户的所有 Session
|
|
||||||
all_sessions = db.query(UserSession).filter(UserSession.user_id == user.id).all()
|
|
||||||
print(f"[get_current_user] 该用户共有 {len(all_sessions)} 个 Session:")
|
|
||||||
for s in all_sessions:
|
|
||||||
print(f" - session_id={s.id}, device_id={s.device_id}, active={s.active}")
|
|
||||||
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="会话已失效或在其他设备登录",
|
detail="会话已失效或在其他设备登录",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"[get_current_user] ✓ Session 验证通过: session_id={session.id}")
|
|
||||||
else:
|
|
||||||
print(f"[get_current_user] ⚠️ Token 中没有 device_id,跳过 Session 校验")
|
|
||||||
|
|
||||||
print("="*60)
|
|
||||||
return username
|
return username
|
||||||
|
|||||||
@@ -8,7 +8,15 @@ SQLALCHEMY_DATABASE_URL = getattr(settings, "DATABASE_URL", "sqlite:///./designe
|
|||||||
# 创建数据库引擎
|
# 创建数据库引擎
|
||||||
engine = create_engine(
|
engine = create_engine(
|
||||||
SQLALCHEMY_DATABASE_URL,
|
SQLALCHEMY_DATABASE_URL,
|
||||||
connect_args={"check_same_thread": False} if SQLALCHEMY_DATABASE_URL.startswith("sqlite") else {}
|
pool_pre_ping=True, # 每次连接前 ping 一下,防止连接断开
|
||||||
|
pool_recycle=3600, # 1小时回收连接
|
||||||
|
pool_size=10, # 连接池大小
|
||||||
|
max_overflow=20, # 最大溢出连接数
|
||||||
|
connect_args={
|
||||||
|
"connect_timeout": 60, # 增加连接超时
|
||||||
|
"read_timeout": 60, # 增加读取超时
|
||||||
|
"write_timeout": 60 # 增加写入超时
|
||||||
|
} if not SQLALCHEMY_DATABASE_URL.startswith("sqlite") else {"check_same_thread": False}
|
||||||
)
|
)
|
||||||
|
|
||||||
# 会话工厂与 ORM 基类
|
# 会话工厂与 ORM 基类
|
||||||
@@ -29,6 +37,8 @@ def init_db():
|
|||||||
from app.models.group import PluginGroup
|
from app.models.group import PluginGroup
|
||||||
from app.models.session import UserSession
|
from app.models.session import UserSession
|
||||||
from app.models.business import FeatureConfig, VipConfig, CheckInConfig, CheckInRecord, PointsHistory
|
from app.models.business import FeatureConfig, VipConfig, CheckInConfig, CheckInRecord, PointsHistory
|
||||||
|
from app.models.logs import PltProcessRecord, UserActionLog, PltPiece
|
||||||
|
from app.models.chat import ChatSession, ChatMessage
|
||||||
Base.metadata.create_all(bind=engine)
|
Base.metadata.create_all(bind=engine)
|
||||||
ensure_migrations()
|
ensure_migrations()
|
||||||
seed_data()
|
seed_data()
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ from fastapi import FastAPI, Request
|
|||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
from app.api.v1 import auth, client, admin, analytics, jsx_demo, admin_config, feature, checkin, user_profile, stats
|
from app.api.v1 import auth, client, admin, analytics, admin_config, feature, checkin, user_profile, stats, algorithm, logs, ai_chat
|
||||||
from app.db import init_db
|
from app.db import init_db
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@@ -59,7 +58,6 @@ app.include_router(auth.router, prefix=f"{settings.API_V1_STR}/auth", tags=["aut
|
|||||||
app.include_router(client.router, prefix=f"{settings.API_V1_STR}/client", tags=["client"])
|
app.include_router(client.router, prefix=f"{settings.API_V1_STR}/client", tags=["client"])
|
||||||
app.include_router(admin.router, prefix=f"{settings.API_V1_STR}/admin", tags=["admin"])
|
app.include_router(admin.router, prefix=f"{settings.API_V1_STR}/admin", tags=["admin"])
|
||||||
app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
|
app.include_router(analytics.router, prefix=f"{settings.API_V1_STR}/analytics", tags=["analytics"])
|
||||||
app.include_router(jsx_demo.router, prefix=f"{settings.API_V1_STR}/jsx_demo", tags=["jsx_demo"])
|
|
||||||
|
|
||||||
# 新增路由
|
# 新增路由
|
||||||
app.include_router(admin_config.router, prefix=settings.API_V1_STR, tags=["admin-config"])
|
app.include_router(admin_config.router, prefix=settings.API_V1_STR, tags=["admin-config"])
|
||||||
@@ -67,6 +65,9 @@ app.include_router(feature.router, prefix=settings.API_V1_STR, tags=["feature"])
|
|||||||
app.include_router(checkin.router, prefix=settings.API_V1_STR, tags=["checkin"])
|
app.include_router(checkin.router, prefix=settings.API_V1_STR, tags=["checkin"])
|
||||||
app.include_router(user_profile.router, prefix=settings.API_V1_STR, tags=["user-profile"])
|
app.include_router(user_profile.router, prefix=settings.API_V1_STR, tags=["user-profile"])
|
||||||
app.include_router(stats.router, prefix=settings.API_V1_STR, tags=["stats"])
|
app.include_router(stats.router, prefix=settings.API_V1_STR, tags=["stats"])
|
||||||
|
app.include_router(algorithm.router, prefix=settings.API_V1_STR, tags=["algorithm"])
|
||||||
|
app.include_router(logs.router, prefix=settings.API_V1_STR, tags=["logs"])
|
||||||
|
app.include_router(ai_chat.router, prefix=settings.API_V1_STR, tags=["ai-chat"])
|
||||||
|
|
||||||
# Health Check
|
# Health Check
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
@@ -77,26 +78,6 @@ def health_check():
|
|||||||
if IS_DEV:
|
if IS_DEV:
|
||||||
# Mount archives directory for download
|
# Mount archives directory for download
|
||||||
app.mount("/download", StaticFiles(directory="archives"), name="download")
|
app.mount("/download", StaticFiles(directory="archives"), name="download")
|
||||||
|
|
||||||
# Mount Shell directory (登录页面)
|
|
||||||
# shell_dir = Path(__file__).parent.parent / "Designer"
|
|
||||||
# if shell_dir.exists():
|
|
||||||
# app.mount("/shell", StaticFiles(directory=str(shell_dir), html=True), name="shell")
|
|
||||||
# print(f"✓ Shell 已挂载 (Dev): {shell_dir}")
|
|
||||||
# else:
|
|
||||||
# # print(f"⚠️ Shell 目录不存在: {shell_dir}")
|
|
||||||
# # print(" 请先运行: cd Designer && npm run build:shell")
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# Mount DesignerCache directory to serve Core application files
|
|
||||||
# designer_cache = Path.home() / "AppData" / "Roaming" / "DesignerCache"
|
|
||||||
# if designer_cache.exists():
|
|
||||||
# app.mount("/core", StaticFiles(directory=str(designer_cache), html=True), name="core")
|
|
||||||
# print(f"✓ Core 已挂载 (Dev): {designer_cache}")
|
|
||||||
# else:
|
|
||||||
# # Create directory if it doesn't exist
|
|
||||||
# designer_cache.mkdir(parents=True, exist_ok=True)
|
|
||||||
# app.mount("/core", StaticFiles(directory=str(designer_cache), html=True), name="core")
|
|
||||||
else:
|
else:
|
||||||
print("ℹ️ Production Mode: Static files are NOT mounted by FastAPI (handled by Caddy/Nginx).")
|
print("ℹ️ Production Mode: Static files are NOT mounted by FastAPI (handled by Caddy/Nginx).")
|
||||||
|
|
||||||
|
|||||||
31
Server/app/models/chat.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
"""
|
||||||
|
AI 对话模型
|
||||||
|
- ChatSession: 对话会话(按用户隔离)
|
||||||
|
- ChatMessage: 对话消息
|
||||||
|
"""
|
||||||
|
from sqlalchemy import Column, Integer, String, DateTime, Text, ForeignKey, func
|
||||||
|
from app.db import Base
|
||||||
|
|
||||||
|
|
||||||
|
class ChatSession(Base):
|
||||||
|
"""对话会话表 — 每个用户可以有多个对话"""
|
||||||
|
__tablename__ = "chat_sessions"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||||
|
username = Column(String(64), nullable=False, index=True)
|
||||||
|
title = Column(String(200), default="新对话") # 对话标题(取首条消息摘要)
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||||
|
|
||||||
|
|
||||||
|
class ChatMessage(Base):
|
||||||
|
"""对话消息表"""
|
||||||
|
__tablename__ = "chat_messages"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
session_id = Column(Integer, ForeignKey("chat_sessions.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||||
|
role = Column(String(20), nullable=False) # user / assistant / system
|
||||||
|
content = Column(Text, nullable=False) # 消息内容
|
||||||
|
tool_calls = Column(Text, nullable=True) # 工具调用 JSON(预留)
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
94
Server/app/models/logs.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
PLT处理记录和用户操作日志模型
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sqlalchemy import Column, Integer, String, DateTime, func, ForeignKey, Text, JSON, Float
|
||||||
|
from sqlalchemy.orm import relationship
|
||||||
|
from app.db import Base
|
||||||
|
|
||||||
|
|
||||||
|
class PltProcessRecord(Base):
|
||||||
|
"""PLT处理记录表"""
|
||||||
|
__tablename__ = "plt_process_records"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||||
|
|
||||||
|
# 文件信息
|
||||||
|
filename = Column(String(255), nullable=True)
|
||||||
|
file_size = Column(Integer, nullable=True) # 字节
|
||||||
|
rotated_filename = Column(String(255), nullable=True) # 旋转对比文件名
|
||||||
|
|
||||||
|
# 处理参数
|
||||||
|
size_labels = Column(JSON, nullable=True) # ["S", "M", "L", ...]
|
||||||
|
dpi = Column(Integer, default=150)
|
||||||
|
rotation = Column(Integer, default=0)
|
||||||
|
|
||||||
|
# 处理结果
|
||||||
|
total_groups = Column(Integer, default=0) # 裁片组数
|
||||||
|
total_pieces = Column(Integer, default=0) # 裁片总数
|
||||||
|
process_time_ms = Column(Integer, nullable=True) # 处理耗时(毫秒)
|
||||||
|
|
||||||
|
# 状态
|
||||||
|
status = Column(String(20), default='success') # success, error
|
||||||
|
error_message = Column(Text, nullable=True)
|
||||||
|
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||||
|
|
||||||
|
# 关联
|
||||||
|
user = relationship("User", backref="plt_records")
|
||||||
|
pieces = relationship("PltPiece", back_populates="record", cascade="all, delete-orphan")
|
||||||
|
|
||||||
|
|
||||||
|
class PltPiece(Base):
|
||||||
|
"""PLT裁片详情表"""
|
||||||
|
__tablename__ = "plt_pieces"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
record_id = Column(Integer, ForeignKey("plt_process_records.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||||
|
|
||||||
|
# 裁片信息
|
||||||
|
group_id = Column(Integer, nullable=False) # 组号
|
||||||
|
size = Column(String(20), nullable=False) # 尺码
|
||||||
|
image_url = Column(String(500), nullable=True) # 七牛云 URL
|
||||||
|
|
||||||
|
# 尺寸信息
|
||||||
|
width_px = Column(Integer, default=0)
|
||||||
|
height_px = Column(Integer, default=0)
|
||||||
|
width_cm = Column(Float, default=0)
|
||||||
|
height_cm = Column(Float, default=0)
|
||||||
|
left_cm = Column(Float, default=0)
|
||||||
|
top_cm = Column(Float, default=0)
|
||||||
|
center_x_cm = Column(Float, default=0)
|
||||||
|
center_y_cm = Column(Float, default=0)
|
||||||
|
|
||||||
|
# 关联
|
||||||
|
record = relationship("PltProcessRecord", back_populates="pieces")
|
||||||
|
|
||||||
|
|
||||||
|
class UserActionLog(Base):
|
||||||
|
"""用户操作日志表"""
|
||||||
|
__tablename__ = "user_action_logs"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, index=True)
|
||||||
|
|
||||||
|
# 会话追踪
|
||||||
|
session_id = Column(String(64), nullable=True, index=True)
|
||||||
|
|
||||||
|
# 操作信息
|
||||||
|
action_type = Column(String(50), nullable=False, index=True) # plt.upload, plt.process, etc.
|
||||||
|
action_params = Column(JSON, nullable=True) # 操作参数
|
||||||
|
page = Column(String(50), nullable=True) # 所在页面
|
||||||
|
|
||||||
|
# 结果
|
||||||
|
result = Column(String(20), default='success') # success, error
|
||||||
|
error_message = Column(Text, nullable=True)
|
||||||
|
|
||||||
|
# 时间
|
||||||
|
duration_ms = Column(Integer, nullable=True) # 操作耗时
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||||
|
|
||||||
|
# 关联
|
||||||
|
user = relationship("User", backref="action_logs")
|
||||||