feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
这个目录放置了一些和 @flow-workflow/fabric-canvas-node-render 可以共用的函数、类型、常量...
因为 @flow-workflow/fabric-canvas-node-render 是一个 node 工程,要注意 share 中不要出现 tsx 文件,否则会导致 node 工程无法编译。

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* 这个文件仅实现 setImageFixed 一个函数就好
* nodejs 图片渲染同样需要计算位置。要在 packages/workflow/nodejs/fabric-render 实现一份功能完全一致的 js 版
*/
import {
type FabricImage,
type FabricObject,
type Group,
type Rect,
} from 'fabric';
import { ImageFixedType, type FabricObjectWithCustomProps } from './typings';
/**
* 调整 img 位置
*/
export const setImageFixed = ({ element }: { element: FabricObject }) => {
const { width, height } = element;
const img = (element as Group).getObjects()[0] as FabricImage;
const { customFixedType } = img as unknown as FabricObjectWithCustomProps;
const borderRect = (element as Group).getObjects()[1] as Rect;
const { strokeWidth = 0 } = borderRect;
// 填充/拉伸时,框适配 group 大小即可
const borderRectWidth = width - strokeWidth;
const borderRectHeight = height - strokeWidth;
borderRect.set({
width: borderRectWidth,
height: borderRectHeight,
left: -width / 2,
top: -height / 2,
});
const { width: originWidth, height: originHeight } = img.getOriginalSize();
/**
* 为什么 +1
* 经过计算后,存储位数有限,不管是 scaleX/Y width/height top/left都会丢失一点点精度
* 这点精度反馈到图片上,就是图片与边框有一点点间隙
* 这里 +1 让图片显示的稍微大一点,弥补精度带来的间隙。
* 弊端:边框会覆盖一点点图片(覆盖多少看缩放比),用户基本无感
*/
const realScaleX = (width - strokeWidth * 2 + 1) / originWidth;
const realScaleY = (height - strokeWidth * 2 + 1) / originHeight;
const minScale = Math.min(realScaleX, realScaleY);
const maxScale = Math.max(realScaleX, realScaleY);
let scaleX = minScale;
let scaleY = minScale;
if (customFixedType === ImageFixedType.FILL) {
scaleX = maxScale;
scaleY = maxScale;
} else if (customFixedType === ImageFixedType.FULL) {
scaleX = realScaleX;
scaleY = realScaleY;
}
const imgLeft = -(originWidth * scaleX) / 2;
const imgTop = -(originHeight * scaleY) / 2;
// 自适应时需要对图片描边
if (customFixedType === ImageFixedType.AUTO) {
borderRect.set({
width: Math.min(borderRectWidth, originWidth * scaleX + strokeWidth),
height: Math.min(borderRectHeight, originHeight * scaleY + strokeWidth),
left: Math.max(-width / 2, imgLeft - strokeWidth),
top: Math.max(-height / 2, imgTop - strokeWidth),
});
}
img.set({
left: imgLeft,
top: imgTop,
width: originWidth,
height: originHeight,
scaleX,
scaleY,
});
};

View File

@@ -0,0 +1,247 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const fonts = [
'1-中等-思源黑体.otf',
'1-常规体-思源黑体.otf',
'1-特细-思源黑体.otf',
'1-粗体-思源黑体.otf',
'1-细体-思源黑体.otf',
'1-黑体-思源黑体.otf',
'10-字语趣淘体.ttf',
'11-字语金农漆书体.ttf',
'12-字语文娱体.ttf',
'13-中等-思源宋体.otf',
'13-常规体-思源宋体.otf',
'13-次粗体-思源宋体.otf',
'13-特细-思源宋体.otf',
'13-粗体-思源宋体.otf',
'13-细体-思源宋体.otf',
'13-黑体-思源宋体.otf',
'14-字语文刻体.ttf',
'15-字语国文楷书.ttf',
'16-字语咏楷体.ttf',
'17-字语纤隶体.ttf',
'18-字语古兰体.ttf',
'19-字语古隶体.ttf',
'2-抖音美好体.ttf',
'20-常规体-字语文圆体.ttf',
'20-粗体-字语文圆体.ttf',
'20-细体-字语文圆体.ttf',
'21-字语趣味像素.ttf',
'22-字语文畅体.ttf',
'23-字语漫雅手书.ttf',
'24-字语香雪煮茶.ttf',
'25-字语逸风手书.ttf',
'26-字语家书体.ttf',
'27-字语青梅硬笔.ttf',
'28-字语明媚体.ttf',
'29-字语萌酱体.ttf',
'3-字语咏乐体.ttf',
'30-字语软糖体.ttf',
'31-中等-源云明体(繁体).ttc',
'31-常规体-源云明体(繁体).ttc',
'31-次粗体-源云明体(繁体).ttc',
'31-特细-源云明体(繁体).ttc',
'31-粗体-源云明体(繁体).ttc',
'31-细体-源云明体(繁体).ttc',
'31-黑体-源云明体(繁体).ttc',
'32-Bold-WixMadefor.otf',
'32-ExtraBold-WixMadefor.otf',
'32-Medium-WixMadefor.otf',
'32-Regular-WixMadefor.otf',
'32-SemiBold-WixMadefor.otf',
'33-Black-Outfit.otf',
'33-ExtraBold-Outfit.otf',
'33-Extralight-Outfit.otf',
'33-Light-Outfit.otf',
'33-Medium-Outfit.otf',
'33-Regular-Outfit.otf',
'33-SemiBold-Outfit.otf',
'33-Thin-Outfit.otf',
'34-110Medium-LibreClarendonNormal.otf',
'34-162Bold-LibreClarendonNormal.otf',
'34-212Black-LibreClarendonNormal.otf',
'34-42Light-LibreClarendonNormal.otf',
'34-68Regular-LibreClarendonNormal.otf',
'35-BoldExt-Coconat.otf',
'35-Regular-Coconat.otf',
'36-Joan.otf',
'37-Bold-Messapia.otf',
'37-Regular-Messapia.otf',
'38-Squatina.otf',
'39-ZYLAAAgoodbook.ttf',
'4-字语咏宏体.ttf',
'40-ZYLAABravery.ttf',
'41-ZYLAADontforget.ttf',
'42-ZYLAAElegance.ttf',
'43-ZYLAAAnemone.ttf',
'44-StoryScript.otf',
'45-ZYLAAIridescent.ttf',
'46-ZYENADelicacy.ttf',
'47-Bolderslant.ttf',
'48-PinyonScript.otf',
'49-ZYLAADeepblue.ttf',
'5-站酷庆科黄油体.ttf',
'50-ZYLAASylph.ttf',
'51-ZYENAFetching.ttf',
'52-ZYLAACosy.ttf',
'53-ZYENAConfectionary.ttf',
'54-ZYENAGambol.ttf',
'55-RubikBubbles.ttf',
'56-Bold-KabinettFraktur.ttf',
'56-Regular-KabinettFraktur.ttf',
'57-RibesBlack.otf',
'58-Bold-DynaPuff.otf',
'58-Medium-DynaPuff.otf',
'58-Regular-DynaPuff.otf',
'58-SemiBold-DynaPuff.otf',
'59-ZYLAAAugenstern.ttf',
'6-字语寂黑体.ttf',
'60-MatrixSans.otf',
'61-MatrixSansPrint.otf',
'62-MatrixSansRaster.otf',
'63-MatrixSansScreen.otf',
'64-MatrixSansVideo.otf',
'7-字语墨黑体.ttf',
'8-字语酷黑体.ttf',
'9-字语趣逗体.ttf',
];
export const fontSvg = [
'1-特细-思源黑体.svg',
'1-细体-思源黑体.svg',
'1-常规体-思源黑体.svg',
'1-中等-思源黑体.svg',
'1-粗体-思源黑体.svg',
'1-黑体-思源黑体.svg',
'1-思源黑体.svg',
'10-字语趣淘体.svg',
'11-字语金农漆书体.svg',
'12-字语文娱体.svg',
'13-特细-思源宋体.svg',
'13-细体-思源宋体.svg',
'13-常规体-思源宋体.svg',
'13-中等-思源宋体.svg',
'13-次粗体-思源宋体.svg',
'13-粗体-思源宋体.svg',
'13-黑体-思源宋体.svg',
'13-思源宋体.svg',
'14-字语文刻体.svg',
'15-字语国文楷书.svg',
'16-字语咏楷体.svg',
'17-字语纤隶体.svg',
'18-字语古兰体.svg',
'19-字语古隶体.svg',
'2-抖音美好体.svg',
'20-细体-字语文圆体.svg',
'20-常规体-字语文圆体.svg',
'20-粗体-字语文圆体.svg',
'20-字语文圆体.svg',
'21-字语趣味像素.svg',
'22-字语文畅体.svg',
'23-字语漫雅手书.svg',
'24-字语香雪煮茶.svg',
'25-字语逸风手书.svg',
'26-字语家书体.svg',
'27-字语青梅硬笔.svg',
'28-字语明媚体.svg',
'29-字语萌酱体.svg',
'3-字语咏乐体.svg',
'30-字语软糖体.svg',
'31-特细-源云明体(繁体).svg',
'31-细体-源云明体(繁体).svg',
'31-常规体-源云明体(繁体).svg',
'31-中等-源云明体(繁体).svg',
'31-次粗体-源云明体(繁体).svg',
'31-粗体-源云明体(繁体).svg',
'31-黑体-源云明体(繁体).svg',
'31-源云明体(繁体).svg',
'32-Regular-WixMadefor.svg',
'32-Medium-WixMadefor.svg',
'32-SemiBold-WixMadefor.svg',
'32-Bold-WixMadefor.svg',
'32-ExtraBold-WixMadefor.svg',
'32-WixMadefor.svg',
'33-Thin-Outfit.svg',
'33-Extralight-Outfit.svg',
'33-Light-Outfit.svg',
'33-Regular-Outfit.svg',
'33-Medium-Outfit.svg',
'33-SemiBold-Outfit.svg',
'33-Bold-Outfit.svg',
'33-Black-Outfit.svg',
'33-ExtraBold-Outfit.svg',
'33-Outfit.svg',
'34-42Light-LibreClarendonNormal.svg',
'34-68Regular-LibreClarendonNormal.svg',
'34-110Medium-LibreClarendonNormal.svg',
'34-162Bold-LibreClarendonNormal.svg',
'34-212Black-LibreClarendonNormal.svg',
'34-LibreClarendonNormal.svg',
'35-Regular-Coconat.svg',
'35-BoldExt-Coconat.svg',
'35-Coconat.svg',
'36-Joan.svg',
'37-Regular-Messapia.svg',
'37-Bold-Messapia.svg',
'37-Messapia.svg',
'38-Squatina.svg',
'39-ZYLAAAgoodbook.svg',
'4-字语咏宏体.svg',
'40-ZYLAABravery.svg',
'41-ZYLAADontforget.svg',
'42-ZYLAAElegance.svg',
'43-ZYLAAAnemone.svg',
'44-StoryScript.svg',
'45-ZYLAAIridescent.svg',
'46-ZYENADelicacy.svg',
'47-Bolderslant.svg',
'48-PinyonScript.svg',
'49-ZYLAADeepblue.svg',
// 站酷庆科黄油体 加载总是失败 ,找不到原因,暂时屏蔽
// '5-站酷庆科黄油体.svg',
'50-ZYLAASylph.svg',
'51-ZYENAFetching.svg',
'52-ZYLAACosy.svg',
'53-ZYENAConfectionary.svg',
'54-ZYENAGambol.svg',
'55-RubikBubbles.svg',
'56-Regular-KabinettFraktur.svg',
'56-Bold-KabinettFraktur.svg',
'56-KabinettFraktur.svg',
'57-RibesBlack.svg',
'58-Regular-DynaPuff.svg',
'58-Medium-DynaPuff.svg',
'58-SemiBold-DynaPuff.svg',
'58-Bold-DynaPuff.svg',
'58-DynaPuff.svg',
'59-ZYLAAAugenstern.svg',
'6-字语寂黑体.svg',
'60-MatrixSans.svg',
'61-MatrixSansPrint.svg',
'62-MatrixSansRaster.svg',
'63-MatrixSansScreen.svg',
'64-MatrixSansVideo.svg',
'7-字语墨黑体.svg',
'8-字语酷黑体.svg',
'9-字语趣逗体.svg',
];
export const fontFamilyFilter = (name: string) => {
const match = name.match(/^\d+-(.*?)\./);
return match ? match[1] : null;
};

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { setImageFixed } from './fabric-image';
export { fonts, fontSvg, fontFamilyFilter } from './font';
// eslint-disable-next-line @coze-arch/no-batch-import-or-export
export * from './typings';

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type FabricObject } from 'fabric';
export const REF_VARIABLE_ID_PREFIX = 'variable';
export interface FabricObjectSchema extends CustomFabricProps {
fontSize?: number;
fontFamily?: string;
fill?: string;
stroke?: string;
strokeWidth?: number;
textAlign?: TextAlign;
width?: number;
height?: number;
lineHeight?: number;
text?: string;
/**
* 图片链接
*/
src?: string;
objects?: FabricObjectSchema[];
}
export interface VariableRef {
variableId: string;
objectId: string;
variableName: string;
}
export interface FabricSchema extends FabricObjectSchema {
width: number;
height: number;
background: string;
objects: FabricObjectSchema[];
customVariableRefs: VariableRef[];
}
/**
* 为什么不用 FabricObject.type
* 因为 fabricSchema.type 跟 fabricObject.type 对不上
* eg Textbox 在 schema 里是 textbox实例化后是 Textbox
*/
export enum Mode {
INLINE_TEXT = 'inline_text',
BLOCK_TEXT = 'block_text',
RECT = 'rect',
TRIANGLE = 'triangle',
CIRCLE = 'ellipse',
STRAIGHT_LINE = 'straight_line',
PENCIL = 'pencil',
IMAGE = 'img',
GROUP = 'group',
}
/**
* 填充和描边
*/
export enum ColorMode {
FILL = 'fill',
STROKE = 'stroke',
}
/**
* 文本对齐方式
*/
export enum TextAlign {
LEFT = 'left',
CENTER = 'center',
RIGHT = 'right',
JUSTIFY = 'justify',
}
/**
* 图片填充方式
*/
export enum ImageFixedType {
AUTO = 'auto',
FILL = 'fill',
FULL = 'full',
}
export interface CustomFabricProps {
customType: Mode;
customId: string;
customFixedHeight?: number;
customFixedType?: ImageFixedType;
/** @deprecated 兼容历史,不可新增消费 */
customVariableName?: string;
[k: string]: unknown;
}
export interface FabricObjectWithCustomProps
extends FabricObject,
CustomFabricProps {}
export const UNKNOWN_VARIABLE_NAME = '__unknown__';