feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
这个目录放置了一些和 @flow-workflow/fabric-canvas-node-render 可以共用的函数、类型、常量...
|
||||
|
||||
因为 @flow-workflow/fabric-canvas-node-render 是一个 node 工程,要注意 share 中不要出现 tsx 文件,否则会导致 node 工程无法编译。
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
247
frontend/packages/workflow/fabric-canvas/src/share/font.ts
Normal file
247
frontend/packages/workflow/fabric-canvas/src/share/font.ts
Normal 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;
|
||||
};
|
||||
20
frontend/packages/workflow/fabric-canvas/src/share/index.ts
Normal file
20
frontend/packages/workflow/fabric-canvas/src/share/index.ts
Normal 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';
|
||||
109
frontend/packages/workflow/fabric-canvas/src/share/typings.ts
Normal file
109
frontend/packages/workflow/fabric-canvas/src/share/typings.ts
Normal 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__';
|
||||
Reference in New Issue
Block a user