Git Product home page Git Product logo

keypsd's Introduction

PSD工具

支持PSD解析和PSD生成.

解析和生成均暂不支持:

  • RGB以外的色彩模式
  • 16/32位深度图像
  • Zip压缩格式下的图像数据.
  • 绘制图层(正常图层)和文本图层之外类型的图层
  • 蒙版

本文档开篇列举了基本API的使用示例, 若你希望直接阅读PSD的解析结果来考虑该框架的解析能力是否适合你的需求, 请直接跳至数据.

图层顺序

你需要特别注意, 解析得到的图层的顺序, 在Photoshop的图层栏中是从下向上排列的.

如果你需要从上向下渲染图层, 则为图层列表layers属性调用Array.prototype.reverse()方法.

使用

keypsd共提供了4个方法供使用: parse, parseFrom, gener, generFrom.

  • parse: 传入ArrayBufferUint8Array, 将其作为PSD数据进行解析.
  • gener: 传入存有PSD数据的对象, 返回Uint8Array作为生成的PSD文件. genergenerate的缩写.
  • parseFrom, generFrom: parsegener异步版本, 支持多种格式(见下文).

其中parseFromgenerFrom只支持浏览器端.

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是一个异步方法, 支持传入以下类型:

  1. 字符串

keypsd会将字符串作为链接进行fetch, 并将结果作为PSD文件解析.

// 获取该链接指向的PSD文件并解析为PSD对象
keypsd.parseFrom("./test.psd")
    // 我们直接将解析结果打印出来
    .then(r=> console.log(r));
  1. 二进制资源

二进制数据, 包括Blob, Uint8ArrayArrayBuffer, 会被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是一个异步方法, 支持传入以下类型:

  1. 字符串:

keypsd会将字符串作为链接进行fetch(), 并将其结果作为图片转化为PSD文件.

// 获取该链接指向的图片并为其生成PSD文件
keypsd.generFrom("./test.jpg")
    // 解析生成的PSD文件并将解析结果打印出来
    .then(r=> console.log(keypsd.parse(r)));
  1. 图像:

keypsd会将该图像直接作为唯一一个图层进行PSD转换.

值得注意的是你并不需要等待Imageonload, 只要其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)));
  1. 二进制资源:

二进制资源包括Uint8Array, ArrayBufferBlob, keypsd会将其作为Imagesrc, 并以图像的模式进行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)));
  1. 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. 如果未指定图层的widthheight, 将缺省为PSD文件本身的widthheight.

对于图层对象, 除了nameimage, 你还可以指定:

  • lefttop: 用于定位图层, 一个有符号整数. 默认值是0.
  • widthheight: 图层自身的大小. 必须是正整数. 默认值是PSD文件本身的widthheight.
  • visible: 布尔值, 表示是否隐藏图层(对应图层栏左侧的小眼睛按钮). 默认是 true.
  • blendMode: 字符串, 必须是CSS mix-blend-mode 的16个属性中的一个.
  • opacity: 0 ~ 255之间的整数之一(包括0255), 代表图层的透明度. 默认是 255.
  • folder: 必须是"open"或者"close", 代表新图层组和关闭图层组. 就像XML一样, 一个图层组需要两个带有folder标志的图层来表示图层组的包含关系, 支持嵌套. 默认值是undefined.
  • text: 文本图层信息. 需要一个chars属性. 见下文text属性的使用说明.

text属性

目前可以使用text属性来创建一个文本图层:

let psd = { width: 16, height: 16, layers: [{
    name: "原神",
    text: { chars: [
        {
            char: "原", 
            size: 15
        }, {
            char: "神", 
            size: 12
        }
    ] }
}] };
keypsd.gener(psd);

chars数组中的成员类型介绍:

  • char: 单个字符(string). 不允许有多个字符出现在char属性中. 不可省略.
  • color: [R, G, B, A]颜色(Array(4) | Uint8Array(4)), 数字范围0~255. 只读取Red, GreenBlue. Alpha将被忽略. 默认为[0, 0, 0, 255].
  • size: 字体大小. 正数, 可以是浮点数(number), 和CSS的font-size的比例一致. 默认16.
  • underline: 是否有下划线(boolean). 默认false.
  • bold: 是否粗体(boolean). 默认false.
  • italic: 是否斜体(boolean). 默认false.

你如果并不打算给每个字符单独安排样式, 可以参考以下实现:

// 要传入的字符串
const MY_STR = "原神\n启动!";
let textpsd = { width: 32, height: 32, layers: [{
    name: "原神の图层",
    // 将该字符串迭代为每个字符组成数组, 并为每个字符包装成字符对象
    text: { chars: Array.from(MY_STR).map(char=> ({
        // 字符
        char, 
        // 以下颜色示例为黄色rgb(200 200 100)
        // alpha干脆不填也没事
        color: [200, 200, 100], 
        // 字体大小
        size: 8, 
        // 加粗
        bold: true
    })) }
}] };
let gened = keypsd.gener(textpsd);

运行 编译

浏览器环境:

该项目无依赖, 运行webpack即可在broswer文件夹下生成keypsd.js.

也可以webpack watch, 以便在broswer文件夹下进行开发和测试.

node环境:

可以直接使用npm test, 来运行src/test/test.js.

keypsd's People

Contributors

bylx666 avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

keypsd's Issues

文本图层解析对单字符长度不明确.

如题, 也就是解析存在emoji的文本图层时会导致整段文字样式乱掉.

另外, 生成PSD时文本图层的charschar的单字符若为emoji或其他无法用单个utf16表示的字符时就会出现乱码.

字体之间, Postscript对family的转换

Photoshop使用了字体的Postscript名称作为PSD的储存方式. 目前前端最有效的, 将其转换为CSS可用的family名称的方法是queryLocalFonts, 然后其兼容性并不理想, 火狐甚至仍未实装.

暂时不考虑字体转换的问题, 至少等火狐实装了这个API再说.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.