Keypsd
JavaScript PSD tool
Install / Use
/learn @Bylx666/KeypsdREADME
PSD工具
支持PSD解析和PSD生成.
解析和生成均暂不支持:
- RGB以外的色彩模式
- 16/32位深度图像
- Zip压缩格式下的图像数据.
- 绘制图层(正常图层)和文本图层之外类型的图层
- 蒙版
本文档开篇列举了基本API的使用示例, 若你希望直接阅读PSD的解析结果来考虑该框架的解析能力是否适合你的需求, 请直接跳至数据.
图层顺序
你需要特别注意, 解析得到的图层的顺序, 在Photoshop的图层栏中是从下向上排列的.
如果你需要从上向下渲染图层, 则为图层列表layers属性调用Array.prototype.reverse()方法.
使用
keypsd共提供了4个方法供使用: parse, parseFrom, gener, generFrom.
parse: 传入ArrayBuffer或Uint8Array, 将其作为PSD数据进行解析.gener: 传入存有PSD数据的对象, 返回Uint8Array作为生成的PSD文件.gener是generate的缩写.parseFrom,generFrom:parse和gener的异步版本, 支持多种格式(见下文).
其中parseFrom和generFrom只支持浏览器端.
Nodejs
以下示例直接将解析的结果的对象作为参数, 重新生成了新的psd.
// 引入parse和gener函数
// parse: Parse 解析, gener: Generate 生成
const { parse, gener } = require("keypsd");
const { readFileSync, writeFileSync } = require("fs");
// 读取psd文件并解析
let file = readFileSync("./src/test/temp.psd");
// parse接受Uint8Array, Buffer或ArrayBuffer
let result = parse(file);
// 递归显示解析结果
console.dir(result, { depth: null });
// 由测试数据生成PSD文件
let gened = gener(result);
// 写入psd文件, 去文件管理器中打开试试看
writeFileSync("./src/test/write.psd", gened);
// 你可以再次解析生成的数据
// parse(gened);
// ...
浏览器
以下示例提供了一个上传按钮, 上传PSD文件后解析出的数据会被输出在界面并打印在控制台. 一个下载按钮将允许你下载通过解析的psd对象重新生成的新psd文件.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>KEYPSD 示例</title>
</head>
<body>
<input type="file" name="upload" id="upload" accept=".psd">
<pre><code id="parse-result"></code></pre>
<a id="download" href="#">下载生成的新PSD</a>
<script src="./keypsd.js"></script>
<script>
// 先获取元素
// 上传按钮
let $upload = document.getElementById("upload");
// 解析结果显示处
let $parseResult = document.getElementById("parse-result");
// 生成的PSD的下载键
let $download = document.getElementById("download");
// 为上传按钮绑定事件
$upload.onchange = async ()=> {
// 使用全局对象`keypsd`的异步函数`parseFrom`解析该文件
let psd = await keypsd.parseFrom($upload.files[0]);
// 打印解析结果
console.log(psd);
// 将Uint8Array显示为`Buffer(length)`并显示在`parseResult`元素上
$parseResult.textContent = JSON.stringify(
psd,
(_, v)=> v instanceof Uint8Array? `Buffer(${v.byteLength})`: v,
2
);
// 由该对象重新创建PSD
// 此时你可以点击下载键下载
let newpsd = keypsd.gener(psd);
$download.download = "generated.psd";
$download.href = URL.createObjectURL(new Blob([ newpsd ]));
}
</script>
</body>
</html>
更详细的解析示例, 包括图层图像和文本图层的处理, 参见index.html.
parseFrom
parseFrom是一个异步方法, 支持传入以下类型:
- 字符串
keypsd会将字符串作为链接进行fetch, 并将结果作为PSD文件解析.
// 获取该链接指向的PSD文件并解析为PSD对象
keypsd.parseFrom("./test.psd")
// 我们直接将解析结果打印出来
.then(r=> console.log(r));
- 二进制资源
二进制数据, 包括Blob, Uint8Array和ArrayBuffer, 会被keypsd作为PSD文件数据解析.
值得注意的是, <input type="file">类型的HTML元素中, 其files[0]的数据类型为File.
而File类型继承了Blob, 可以直接作为generFrom的参数. 因此我认为该调用方式对于文件拖拽和文件上传实现十分实用.
<!-- 绑定文件上传事件 -->
<input type="file" onchange="upload()" accept=".psd">
<script>
async function upload() {
// 通过target属性获取当前事件的元素, 并读取文件内容
let file = event.target.files[0];
// 传入parseFrom并打印解析结果
console.log(await keypsd.parseFrom(file));
}
</script>
generFrom
generFrom是一个异步方法, 支持传入以下类型:
- 字符串:
keypsd会将字符串作为链接进行fetch(), 并将其结果作为图片转化为PSD文件.
// 获取该链接指向的图片并为其生成PSD文件
keypsd.generFrom("./test.jpg")
// 解析生成的PSD文件并将解析结果打印出来
.then(r=> console.log(keypsd.parse(r)));
- 图像:
keypsd会将该图像直接作为唯一一个图层进行PSD转换.
值得注意的是你并不需要等待Image的onload, 只要其src设置正确即可.
由于PSD数据来源自canvas, 而大部分跨域Image会导致canvas无法导出图像数据, 因此对图像链接存在跨域限制. 详情见MDN: 画布污染.
// 创建新的Image元素
let img = new Image();
// 指定图像链接(存在跨域限制)
img.src = "./test.jpg";
// 无需等待img.onload, 直接传入generFrom
keypsd.generFrom(img)
// 将生成的PSD文件解析并打印
.then(r=> console.log(keypsd.parse(r)));
- 二进制资源:
二进制资源包括Uint8Array, ArrayBuffer和Blob, keypsd会将其作为Image的src, 并以图像的模式进行PSD生成.
值得注意的是, <input type="file">类型的HTML元素中, 其files[0]的数据类型为File.
而File类型继承了Blob, 可以直接作为generFrom的参数. 因此我认为该调用方式对于文件拖拽和文件上传实现十分实用.
fetch("./test.jpg")
// 将fetch结果作为Blob
.then(response=> response.blob())
// 解析该Blob对象表达的PSD文件
.then(blob=> keypsd.generFrom(blob))
// 为了测试生成的文件的可用性, 将其传入parse再次解析
.then(r=> console.log(keypsd.parse(r)));
- Canvas
直接将Canvas元素的图像作为图层生成PSD文件.
<canvas width="40" height="40" id="cv"></canvas>
<script>
let cv = document.getElementById("cv");
// 在画布上写点东西
cv.getContext("2d").fillText("2333", 0, 20);
// 直接将其传入generFrom
keypsd.generFrom(cv)
// 解析生成的PSD并打印
.then(r=> console.log(keypsd.parse(r)));
</script>
数据
你可能会担心你并没有那么多数据用来作为生成psd的参数, 但事实上传入生成psd的函数的参数要求十分宽松.
以下是demo数据, 仅供直观化参考.
具体数据类型和使用请参见index.d.ts.
parse结果参考:
以下psd拥有一个图层组, 一个文本图层和一个普通图层.
psd的每个图层组需要两个图层的位置, 因此以下结果中图层数量为4个.
({
// 以下三个属性为固定值
channels: 3,
depth: 8,
mode: 'RGB',
// 正整数width和height
height: 16,
width: 16,
// 图层列表
layers: [
{
// 4个定位用的整数, width和height为正整数.
top: 0,
left: 0,
width: 0,
height: 0,
// 字符串, 作为图层名称
name: '</Layer group>',
// 可见性, 就是图层左侧的小眼睛按钮状态
visible: true,
// CSS mix-blend-mode的16属性之一, 参见(https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode).
blendMode: 'normal',
// 8位无符号整数(0~255), 代表图层的透明度
opacity: 255,
// 通道, 目前必定为id为`-1`到`2`的4个通道, 分别代表argb.
channels: [
{ id: -1, dataLen: 0 },
{ id: 0, dataLen: 0 },
{ id: 1, dataLen: 0 },
{ id: 2, dataLen: 0 }
],
// 由photoshop为图层生成的id, 不唯一, 不可靠, 不建议使用.
id: 943868237,
// 只要有这个属性就代表这是一个标记图层组的图层, 只会是"open"和"close"两个值.
// 想象带这个属性的图层是一个xml元素, "open"图层就是`<folder>`, "close"图层就是`</folder>`.
// 区别在于, 我们往往只显示"open"的图层作为图层组, "close"图层往往不会被显示出来.
// 另外, 由于得到的图层顺序是图层栏中从下往上的顺序, 因此你会先遇到"close", 之后才有"open".
folder: 'close',
// 由于该图层只是个标记图层组结束的标志, 并不储存图像, 且width和height都是0
// 该image属性只会是一个空的Uint8Array.
image: Uint8Array(0)
},
{
// 该图层没有folder和text属性, 则是普通的图层, 会有图像信息
top: 0,
left: 0,
width: 16,
height: 16,
name: '图层 0',
visible: true,
blendMode: 'normal',
opacity: 255,
channels: [
{ id: -1, dataLen: 64 },
{ id: 0, dataLen: 256 },
{ id: 1, dataLen: 256 },
{ id: 2, dataLen: 256 }
],
id: 943868237,
// 一个图层的图像大小必定为**图层**的长乘宽乘4, 代表了其`RGBA`数据
// 可以直接作为canvas ImageData的数据源
image: Uint8Array(1024)
},
{
// 该图层是文本图层, 因为它有text属性
top: 3,
left: 4,
width: 9,
height: 10,
name: 'a',
visible: true,
blendMode: 'normal',
opacity: 255,
channels: [
{ id: -1, dataLen: 90 },
{ id: 0, dataLen: 90 },
{ id: 1, dataLen: 90 },
{ id: 2, dataLen: 90 }
],
// text属性描述了该图层包含的文本和样式信息
text: {
// 固定6个浮点数, 代表了其变换数据. 参见:
// https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/setTransform
transform: Float64Array(6) [
1,
0,
0,
1,
3.308916720099315,
12.438202409238862
],
// psd中对该文字的描述信息, 可用性不高
raw: {...},
// 该图层的所有字符
text: 'a\n',
// 字符的样式
chars: [
{
char: 'a',
// [R, G, B, A]
// 可以通过`rgb(${color[0]} ${color[1]} ${color[2]})`转成字符串.
color: Uint8Array(4) [ 82, 159, 70, 255 ],
// Postscript字体名, **无法**直接用于`font-family`,
// 可能需要`queryLocalFonts`函数来查询其`family`的值
font: 'MicrosoftJhengHeiUIRegular',
// 字体大小
size: 16,
// 下划线, 粗体, 斜体
underline: false,
bold: true,
italic: true
},
{
char: '\n',
color: Uint8Array(4) [ 82, 159, 70, 255 ],
font: 'MicrosoftJhengHeiUIRegular',
size: 16,
underline: false,
bold: true,
italic: true
},
text: 'a\n'
]
},
id: 943868237,
// 该文本图层的栅格化数据
// psd很有可能没有写入该数据, 而是用0填充了长度
image: Uint8Array(360)
},
{
top: 0,
left: 0,
width: 0,
height: 0,
name: '组 1',
visible: true,
blendMode: 'normal',
opacity: 255,
channels: [
{ id: -1, dataLen: 0 },
{ id: 0, dataLen: 0 },
{ id: 1, dataLen: 0 },
{ id: 2, dataLen: 0 }
],
id: 943868237,
// 照应第一个图层
folder: 'open',
image: Uint8Array(0)
}
]
})
gener参数参考:
最小的psd对象可以是:
let psd = { width: 16, height: 16, layers: [ {} ] };
keypsd.gener(psd);
layers的数组中至少需要一个对象, 如果写成layers: []的话, Photoshop是不认这个文件的.
图层图像
你可以传入一个图像数据(通常由canvas.getContext("2d").getImageData(..)得到)直接将其转为psd:
...
let data = context.getImageData(0, 0, 16, 16).data;
let psd = { width: 16, height: 16, layers: [{
name: "新图层",
image: data
}] };
keypsd.gener(psd);
name: 字符串, 代表图层名称.image:image的长度必须是图层的width * height * 4. 如果未指定图层的width和height, 将缺省为PSD文件本身的width和height.
对于图层对象, 除了name和image, 你还可以指定:
left和top: 用于定位图层, 一个有符号整数. 默认值是0.width和height: 图层自身的大小. 必须是正整数. 默认值是PSD文件本身的width和height.visible: 布尔值, 表示
Related Skills
node-connect
342.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
342.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.7kCommit, push, and open a PR
