eufymake-empf
v2.0.0
Published
Browser-first TypeScript generator and parser for encrypted eufyMake Studio EMPF projects
Maintainers
Readme
eufymake-empf
浏览器优先、与 Node.js 无关的 TypeScript EMPF 库,用于生成和解析 eufyMake Studio 4.x 离线工程。
生成格式为:
eufyMake TLV header + AES-256-GCM(nonce + ZIP + auth tag)
核心库支持普通彩色图、白墨/彩墨/光油层数、自定义灰度深度图和 Texture Layer。格式已经通过 macOS eufyMake Studio 4.0.4 实机打开和自动重存测试。
架构
src/ 浏览器库,只使用 Web 标准 API
cli/ Node.js 命令行入口和文件系统适配
scripts/ 构建期浏览器兼容检查
test/ 格式回归测试src/ 中没有 node: 导入、Buffer、process、文件系统或 sharp。二进制 API 使用 Uint8Array、ArrayBuffer 和 Blob,加密使用 Web Crypto。
安装
npm install eufymake-empf浏览器使用
坐标原点是打印平面左上角;位置和尺寸均使用毫米。
彩图和深度图以 3 cm 宽度等比居中
下面的例子把彩色图片和对应深度图放到迷你板(335 × 90 mm)上。彩色图片的打印宽度固定为 3 cm,即 30 mm;打印高度根据原图宽高比自动计算,图片整体位于画板正中央。
深度图必须和彩图具有相同的像素宽高。深度图可以是浏览器能够解码的图片格式,imageToPng() 会将其转换为 Studio 所需的 PNG。
import {
EmpfGenerator,
InkModeEnum,
PRINT_BEDS,
PrintBedEnum,
imageToPng,
readImageMetadata,
} from "eufymake-empf";
const colorFile = colorInput.files![0];
const depthFile = depthInput.files![0];
// 读取彩图的原始像素尺寸,用它计算打印高度。
const colorBytes = new Uint8Array(await colorFile.arrayBuffer());
const { width: pixelWidth, height: pixelHeight } = readImageMetadata(colorBytes);
// 库的尺寸单位是 mm:3 cm = 30 mm。
const imageWidthMm = 30;
const imageHeightMm = imageWidthMm * (pixelHeight / pixelWidth);
const bed = PRINT_BEDS[PrintBedEnum.miniFlatbed];
if (imageWidthMm > bed.widthMm || imageHeightMm > bed.heightMm) {
throw new Error("等比缩放后的图片无法放入迷你板");
}
// addImage() 接收图片左上角坐标;用两侧剩余空间的一半实现居中。
const xMm = (bed.widthMm - imageWidthMm) / 2;
const yMm = (bed.heightMm - imageHeightMm) / 2;
// Studio 的自定义深度图必须使用 PNG。
const depthPng = await imageToPng(depthFile);
const generator = new EmpfGenerator({
printBed: PrintBedEnum.miniFlatbed,
projectName: "3 cm centered texture",
});
await generator.addImage(
colorBytes,
xMm,
yMm,
imageWidthMm,
imageHeightMm,
{
inkMode: InkModeEnum.cmyk,
cmykLayers: 1,
whiteLayers: 3,
texture: {
depthMap: depthPng,
thickness: 5,
gamma: 7,
invert: false,
},
},
);
const empfBlob = await generator.toBlob();
const downloadUrl = URL.createObjectURL(empfBlob);
const anchor = document.createElement("a");
anchor.href = downloadUrl;
anchor.download = "centered-texture.empf";
anchor.click();
URL.revokeObjectURL(downloadUrl);例如原图是 1200 × 800 px,打印高度会是:
30 × (800 / 1200) = 20 mm最终图片范围为 x = 152.5 ~ 182.5 mm、y = 35 ~ 55 mm,正好位于 335 × 90 mm 迷你板的中心。图片打印尺寸只取决于传给 addImage() 的毫米尺寸,与图片 DPI 元数据无关。
如果不需要缩略图,可以不传 thumbnail;工程中会写入一个透明占位 PNG。
内存 API
const bytes: Uint8Array = await generator.toUint8Array();
const project = await parseEmpf(bytes);
const zipBytes: Uint8Array = await decryptEmpf(bytes);
// File 和 Blob 也可以直接解析。
const uploadedProject = await parseEmpf(fileInput.files![0]);因为 Web Crypto 是异步 API,encryptEmpf() 和 decryptEmpf() 也都是异步函数。
Node CLI
CLI 是 cli/ 下的独立 workspace 包,拥有自己的 package.json 和 Node 依赖,不会进入浏览器库的安装内容或依赖图。Node CLI 需要 Node.js 20 或更高版本。
node cli/dist/empf.js generate \
--image color.png \
--depth-map depth.png \
--output texture.empf \
--bed mini \
--x 122.5 --y 0 --width 90 --height 90 \
--white-layers 3 --cmyk-layers 1 \
--thickness 5 --gamma 7
node cli/dist/empf.js inspect texture.empf
node cli/dist/empf.js decrypt texture.empf --output texture.zip本地开发:
pnpm install
pnpm check
pnpm test
pnpm build
node cli/dist/empf.js inspect test.empfpnpm check 会同时执行:
- 纯浏览器库 TypeScript 检查,且不加载 Node 类型;
- CLI 的独立 Node 类型检查;
src/Node API 静态扫描;platform: browser的完整打包测试。
已确认的 4.0.4 兼容要点
- 文件头长度为 88 字节,TLV 字段记录格式版本、密钥版本、
offline、application/zip和内部文件名。 - AES-GCM 使用 12 字节随机 nonce 和 16 字节认证标签。
- ZIP 使用
STORE,包含项目信息、Fabric 5.3.0 画布、缩略图和字体映射。 - 画布单位为 50 DPI,即
50 / 25.4单位/mm。 - 自定义 Texture 对象内嵌
src彩图与grayscale深度图,并设置纹理、厚度、Gamma 和墨层字段。
实机验收中,Studio 4.0.4 成功解密生成文件、创建工程缩略图、载入 Texture 对象,并自动重存为新的加密 EMPF。重存文件保留了彩图、深度图、pattern 纹理、厚度、墨层、位置和缩放;Studio 自身在该次重存时省略了 gamma 字段,生成器仍按原生 test.empf 结构写入该字段。
这里使用的是 Studio 离线工程所需的内置离线密钥。云端文件可能使用不同的用户密钥,不属于这个库的目标范围。
