refactor: 收口 BangDream 文本画布规格
This commit is contained in:
parent
ef1140bd22
commit
3969d3e4e2
@ -14,7 +14,7 @@
|
|||||||
## 当前事实
|
## 当前事实
|
||||||
|
|
||||||
- Tsugu 源码目录:`src/qqbot/plugins/bangDream/tsugu`
|
- Tsugu 源码目录:`src/qqbot/plugins/bangDream/tsugu`
|
||||||
- TS 文件:初始基线 92;当前 `tsugu` 源码 127
|
- TS 文件:初始基线 92;当前 `tsugu` 源码 128
|
||||||
- 函数节点:481,其中稳定函数 410,匿名/内联回调 71
|
- 函数节点:481,其中稳定函数 410,匿名/内联回调 71
|
||||||
- 源码 JSDoc:稳定函数 410/410 已覆盖
|
- 源码 JSDoc:稳定函数 410/410 已覆盖
|
||||||
- 变量声明:1896
|
- 变量声明:1896
|
||||||
@ -358,6 +358,8 @@ export interface TsuguHook {
|
|||||||
- 已新增 `detail-block-spec.spec.ts`,覆盖歌曲详情尺寸、角色/玩家详情尺寸和 meta 相对百分比舍入;本地生成 `detail-spec-song-136.jpg`、`detail-spec-event-50.jpg`、`detail-spec-character-1.jpg`,验证详情区块规格收口后查曲/查活动/查角色图片输出正常。
|
- 已新增 `detail-block-spec.spec.ts`,覆盖歌曲详情尺寸、角色/玩家详情尺寸和 meta 相对百分比舍入;本地生成 `detail-spec-song-136.jpg`、`detail-spec-event-50.jpg`、`detail-spec-character-1.jpg`,验证详情区块规格收口后查曲/查活动/查角色图片输出正常。
|
||||||
- 已新增 `render-blocks/list-frame-spec.ts`,收口通用列表行、tips、横向合并列、居中图片列表和左侧竖线的字号、间距、兜底尺寸与布局计算;`list-frame.ts` 改为消费 spec,服务器分组、换行和绘制顺序保持不变。
|
- 已新增 `render-blocks/list-frame-spec.ts`,收口通用列表行、tips、横向合并列、居中图片列表和左侧竖线的字号、间距、兜底尺寸与布局计算;`list-frame.ts` 改为消费 spec,服务器分组、换行和绘制顺序保持不变。
|
||||||
- 已新增 `list-frame-spec.spec.ts`,覆盖列表正文宽度、标签/tips 偏移、合并列宽、居中图片换行和左侧竖线尺寸;本地生成 `list-frame-spec-song-136.jpg`、`list-frame-spec-event-50.jpg`、`list-frame-spec-character-1.jpg`,验证列表框架规格收口后查曲/查活动/查角色图片输出正常。
|
- 已新增 `list-frame-spec.spec.ts`,覆盖列表正文宽度、标签/tips 偏移、合并列宽、居中图片换行和左侧竖线尺寸;本地生成 `list-frame-spec-song-136.jpg`、`list-frame-spec-event-50.jpg`、`list-frame-spec-character-1.jpg`,验证列表框架规格收口后查曲/查活动/查角色图片输出正常。
|
||||||
|
- 已新增 `canvas/text-spec.ts`,收口文本默认字号、行高比例、baseline、空画布尺寸、混排间距和内联图片缩放计算;`canvas/text.ts` 改为消费 spec,原有文本换行和混排拆分逻辑保持不变。
|
||||||
|
- 已新增 `text-spec.spec.ts`,覆盖行高/间距比例、baseline、内联图片缩放和空/单行/多行画布尺寸;本地生成 `text-spec-song-136.jpg`、`text-spec-event-50.jpg`、`text-spec-character-1.jpg`,验证文本规格收口后查曲/查活动/查角色图片输出正常。
|
||||||
|
|
||||||
### Phase 6:策略 policy 和时间/档线规则
|
### Phase 6:策略 policy 和时间/档线规则
|
||||||
|
|
||||||
|
|||||||
121
src/qqbot/plugins/bangDream/tsugu/canvas/text-spec.ts
Normal file
121
src/qqbot/plugins/bangDream/tsugu/canvas/text-spec.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
interface ImageLike {
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TextCanvasSizeOptions {
|
||||||
|
lineHeight: number;
|
||||||
|
maxWidth: number;
|
||||||
|
numberOfLines: number;
|
||||||
|
singleLineWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BANGDREAM_TEXT_SPEC = {
|
||||||
|
canvas: {
|
||||||
|
emptyWidth: 1,
|
||||||
|
measureHeight: 1,
|
||||||
|
measureWidth: 1,
|
||||||
|
},
|
||||||
|
font: {
|
||||||
|
baseline: 'alphabetic',
|
||||||
|
defaultSize: 40,
|
||||||
|
},
|
||||||
|
inlineImage: {
|
||||||
|
baselineTextOffsetRatio: 1 / 3,
|
||||||
|
baselineVerticalCenterRatio: 1 / 2,
|
||||||
|
},
|
||||||
|
line: {
|
||||||
|
baselineLineRatio: 1 / 2,
|
||||||
|
baselineTextRatio: 1 / 3,
|
||||||
|
heightRatio: 4 / 3,
|
||||||
|
spacingRatio: 1 / 3,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type BangDreamTextFont = 'FangZhengHeiTi' | 'old' | 'default';
|
||||||
|
|
||||||
|
export type BangDreamTextWithImageFont = 'default' | 'old';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算默认文本行高。
|
||||||
|
*
|
||||||
|
* @param textSize - 文本字号。
|
||||||
|
*/
|
||||||
|
export function getTextLineHeight(textSize: number) {
|
||||||
|
return textSize * BANGDREAM_TEXT_SPEC.line.heightRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算文本和图片混排默认间距。
|
||||||
|
*
|
||||||
|
* @param textSize - 文本字号。
|
||||||
|
*/
|
||||||
|
export function getTextInlineSpacing(textSize: number) {
|
||||||
|
return textSize * BANGDREAM_TEXT_SPEC.line.spacingRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算文本绘制 baseline。
|
||||||
|
*
|
||||||
|
* @param lineHeight - 行高。
|
||||||
|
* @param textSize - 文本字号。
|
||||||
|
*/
|
||||||
|
export function getTextBaselineY(lineHeight: number, textSize: number) {
|
||||||
|
return (
|
||||||
|
lineHeight * BANGDREAM_TEXT_SPEC.line.baselineLineRatio +
|
||||||
|
textSize * BANGDREAM_TEXT_SPEC.line.baselineTextRatio
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算内联图片按文本字号缩放后的宽度。
|
||||||
|
*
|
||||||
|
* @param image - 图片尺寸。
|
||||||
|
* @param textSize - 文本字号。
|
||||||
|
*/
|
||||||
|
export function getInlineImageWidth(image: ImageLike, textSize: number) {
|
||||||
|
return (textSize * image.width) / image.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算内联图片绘制 Y 坐标。
|
||||||
|
*
|
||||||
|
* @param baselineY - 当前文本 baseline。
|
||||||
|
* @param textSize - 文本字号。
|
||||||
|
*/
|
||||||
|
export function getInlineImageY(baselineY: number, textSize: number) {
|
||||||
|
return (
|
||||||
|
baselineY -
|
||||||
|
textSize * BANGDREAM_TEXT_SPEC.inlineImage.baselineTextOffsetRatio -
|
||||||
|
textSize * BANGDREAM_TEXT_SPEC.inlineImage.baselineVerticalCenterRatio
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算文本画布尺寸。
|
||||||
|
*
|
||||||
|
* @param options - 行数、行高、最大宽度和单行宽度。
|
||||||
|
*/
|
||||||
|
export function createTextCanvasSize({
|
||||||
|
lineHeight,
|
||||||
|
maxWidth,
|
||||||
|
numberOfLines,
|
||||||
|
singleLineWidth = BANGDREAM_TEXT_SPEC.canvas.emptyWidth,
|
||||||
|
}: TextCanvasSizeOptions) {
|
||||||
|
if (numberOfLines === 0) {
|
||||||
|
return {
|
||||||
|
height: lineHeight,
|
||||||
|
width: BANGDREAM_TEXT_SPEC.canvas.emptyWidth,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (numberOfLines === 1) {
|
||||||
|
return {
|
||||||
|
height: lineHeight,
|
||||||
|
width: singleLineWidth,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
height: lineHeight * numberOfLines,
|
||||||
|
width: maxWidth,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -6,6 +6,17 @@ import {
|
|||||||
} from 'skia-canvas';
|
} from 'skia-canvas';
|
||||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||||
|
import {
|
||||||
|
BANGDREAM_TEXT_SPEC,
|
||||||
|
BangDreamTextFont,
|
||||||
|
BangDreamTextWithImageFont,
|
||||||
|
createTextCanvasSize,
|
||||||
|
getInlineImageWidth,
|
||||||
|
getInlineImageY,
|
||||||
|
getTextBaselineY,
|
||||||
|
getTextInlineSpacing,
|
||||||
|
getTextLineHeight,
|
||||||
|
} from '@/qqbot/plugins/bangDream/tsugu/canvas/text-spec';
|
||||||
|
|
||||||
FontLibrary.use('old', [getBangDreamAssetPath('fontOld')]);
|
FontLibrary.use('old', [getBangDreamAssetPath('fontOld')]);
|
||||||
FontLibrary.use('FangZhengHeiTi', [
|
FontLibrary.use('FangZhengHeiTi', [
|
||||||
@ -18,7 +29,7 @@ interface WrapTextOptions {
|
|||||||
maxWidth: number;
|
maxWidth: number;
|
||||||
lineHeight?: number;
|
lineHeight?: number;
|
||||||
color?: string;
|
color?: string;
|
||||||
font?: 'FangZhengHeiTi' | 'old' | 'default';
|
font?: BangDreamTextFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
//画文字,自动换行
|
//画文字,自动换行
|
||||||
@ -30,30 +41,29 @@ interface WrapTextOptions {
|
|||||||
*/
|
*/
|
||||||
export function drawText({
|
export function drawText({
|
||||||
text,
|
text,
|
||||||
textSize = 40,
|
textSize = BANGDREAM_TEXT_SPEC.font.defaultSize,
|
||||||
maxWidth,
|
maxWidth,
|
||||||
lineHeight = (textSize * 4) / 3,
|
lineHeight = getTextLineHeight(textSize),
|
||||||
color = BANGDREAM_RENDER_THEME.color.primaryText,
|
color = BANGDREAM_RENDER_THEME.color.primaryText,
|
||||||
font = BANGDREAM_RENDER_THEME.font.body,
|
font = BANGDREAM_RENDER_THEME.font.body,
|
||||||
}: WrapTextOptions): Canvas {
|
}: WrapTextOptions): Canvas {
|
||||||
const wrappedTextData = wrapText({ text, maxWidth, lineHeight, textSize });
|
const wrappedTextData = wrapText({ text, maxWidth, lineHeight, textSize });
|
||||||
let canvas: Canvas;
|
let singleLineWidth: number | undefined;
|
||||||
if (wrappedTextData.numberOfLines == 0) {
|
if (wrappedTextData.numberOfLines == 1) {
|
||||||
canvas = new Canvas(1, lineHeight);
|
const ctx = createMeasureContext();
|
||||||
} else if (wrappedTextData.numberOfLines == 1) {
|
|
||||||
canvas = new Canvas(1, 1);
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
setFontStyle(ctx, textSize, font);
|
setFontStyle(ctx, textSize, font);
|
||||||
const width = (maxWidth = ctx.measureText(
|
singleLineWidth = ctx.measureText(wrappedTextData.wrappedText[0]).width;
|
||||||
wrappedTextData.wrappedText[0],
|
|
||||||
).width);
|
|
||||||
canvas = new Canvas(width, lineHeight);
|
|
||||||
} else {
|
|
||||||
canvas = new Canvas(maxWidth, lineHeight * wrappedTextData.numberOfLines);
|
|
||||||
}
|
}
|
||||||
|
const canvasSize = createTextCanvasSize({
|
||||||
|
lineHeight,
|
||||||
|
maxWidth,
|
||||||
|
numberOfLines: wrappedTextData.numberOfLines,
|
||||||
|
singleLineWidth,
|
||||||
|
});
|
||||||
|
const canvas = new Canvas(canvasSize.width, canvasSize.height);
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
let y = lineHeight / 2 + textSize / 3;
|
let y = getTextBaselineY(lineHeight, textSize);
|
||||||
ctx.textBaseline = 'alphabetic';
|
ctx.textBaseline = BANGDREAM_TEXT_SPEC.font.baseline;
|
||||||
setFontStyle(ctx, textSize, font);
|
setFontStyle(ctx, textSize, font);
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
const wrappedText = wrappedTextData.wrappedText;
|
const wrappedText = wrappedTextData.wrappedText;
|
||||||
@ -71,14 +81,13 @@ export function drawText({
|
|||||||
*/
|
*/
|
||||||
export function wrapText({
|
export function wrapText({
|
||||||
text,
|
text,
|
||||||
textSize,
|
textSize = BANGDREAM_TEXT_SPEC.font.defaultSize,
|
||||||
maxWidth,
|
maxWidth,
|
||||||
font = 'old',
|
font = 'old',
|
||||||
}: WrapTextOptions) {
|
}: WrapTextOptions) {
|
||||||
const canvas = new Canvas(1, 1);
|
const ctx = createMeasureContext();
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
const temp = text.split('\n');
|
const temp = text.split('\n');
|
||||||
ctx.textBaseline = 'alphabetic';
|
ctx.textBaseline = BANGDREAM_TEXT_SPEC.font.baseline;
|
||||||
setFontStyle(ctx, textSize, font);
|
setFontStyle(ctx, textSize, font);
|
||||||
|
|
||||||
for (let i = 0; i < temp.length; i++) {
|
for (let i = 0; i < temp.length; i++) {
|
||||||
@ -118,7 +127,7 @@ interface TextWithImagesOptions {
|
|||||||
content: (string | Canvas | Image)[];
|
content: (string | Canvas | Image)[];
|
||||||
spacing?: number;
|
spacing?: number;
|
||||||
color?: string;
|
color?: string;
|
||||||
font?: 'default' | 'old';
|
font?: BangDreamTextWithImageFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 画文字包含图片
|
// 画文字包含图片
|
||||||
@ -128,11 +137,11 @@ interface TextWithImagesOptions {
|
|||||||
* @param options1 - options1参数。
|
* @param options1 - options1参数。
|
||||||
*/
|
*/
|
||||||
export function drawTextWithImages({
|
export function drawTextWithImages({
|
||||||
textSize = 40,
|
textSize = BANGDREAM_TEXT_SPEC.font.defaultSize,
|
||||||
maxWidth,
|
maxWidth,
|
||||||
lineHeight = (textSize * 4) / 3,
|
lineHeight = getTextLineHeight(textSize),
|
||||||
content,
|
content,
|
||||||
spacing = textSize / 3,
|
spacing = getTextInlineSpacing(textSize),
|
||||||
color = BANGDREAM_RENDER_THEME.color.primaryText,
|
color = BANGDREAM_RENDER_THEME.color.primaryText,
|
||||||
font = BANGDREAM_RENDER_THEME.font.body,
|
font = BANGDREAM_RENDER_THEME.font.body,
|
||||||
}: TextWithImagesOptions) {
|
}: TextWithImagesOptions) {
|
||||||
@ -144,36 +153,33 @@ export function drawTextWithImages({
|
|||||||
spacing,
|
spacing,
|
||||||
});
|
});
|
||||||
const wrappedText = wrappedTextData.wrappedText;
|
const wrappedText = wrappedTextData.wrappedText;
|
||||||
let canvas: Canvas;
|
let singleLineWidth: number | undefined;
|
||||||
if (wrappedTextData.numberOfLines == 0) {
|
if (wrappedTextData.numberOfLines == 1) {
|
||||||
canvas = new Canvas(1, lineHeight);
|
const ctx = createMeasureContext();
|
||||||
}
|
|
||||||
//单行文字,宽度为第一行的宽度
|
|
||||||
else if (wrappedTextData.numberOfLines == 1) {
|
|
||||||
canvas = new Canvas(1, 1);
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
setFontStyle(ctx, textSize, font);
|
setFontStyle(ctx, textSize, font);
|
||||||
let Width = 0;
|
let width = 0;
|
||||||
for (let n = 0; n < wrappedText[0].length; n++) {
|
for (let n = 0; n < wrappedText[0].length; n++) {
|
||||||
if (typeof wrappedText[0][n] === 'string') {
|
if (typeof wrappedText[0][n] === 'string') {
|
||||||
Width += ctx.measureText(wrappedText[0][n] as string).width;
|
width += ctx.measureText(wrappedText[0][n] as string).width;
|
||||||
} else {
|
} else {
|
||||||
//等比例缩放图片,至高度与textSize相同
|
//等比例缩放图片,至高度与textSize相同
|
||||||
const tempImage = wrappedText[0][n] as Canvas | Image;
|
const tempImage = wrappedText[0][n] as Canvas | Image;
|
||||||
const tempWidth = (textSize * tempImage.width) / tempImage.height; //等比例缩放到高度与字体大小相同后,图片宽度
|
width += getInlineImageWidth(tempImage, textSize);
|
||||||
Width += tempWidth;
|
|
||||||
}
|
}
|
||||||
Width += spacing;
|
width += spacing;
|
||||||
}
|
}
|
||||||
canvas = new Canvas(Width - spacing, lineHeight);
|
singleLineWidth = width - spacing;
|
||||||
}
|
|
||||||
//多行文字
|
|
||||||
else {
|
|
||||||
canvas = new Canvas(maxWidth, lineHeight * wrappedTextData.numberOfLines);
|
|
||||||
}
|
}
|
||||||
|
const canvasSize = createTextCanvasSize({
|
||||||
|
lineHeight,
|
||||||
|
maxWidth,
|
||||||
|
numberOfLines: wrappedTextData.numberOfLines,
|
||||||
|
singleLineWidth,
|
||||||
|
});
|
||||||
|
const canvas = new Canvas(canvasSize.width, canvasSize.height);
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
let y = lineHeight / 2 + textSize / 3;
|
let y = getTextBaselineY(lineHeight, textSize);
|
||||||
ctx.textBaseline = 'alphabetic';
|
ctx.textBaseline = BANGDREAM_TEXT_SPEC.font.baseline;
|
||||||
setFontStyle(ctx, textSize, font);
|
setFontStyle(ctx, textSize, font);
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
for (let i = 0; i < wrappedText.length; i++) {
|
for (let i = 0; i < wrappedText.length; i++) {
|
||||||
@ -185,11 +191,11 @@ export function drawTextWithImages({
|
|||||||
} else {
|
} else {
|
||||||
//等比例缩放图片,至高度与textSize相同
|
//等比例缩放图片,至高度与textSize相同
|
||||||
const tempImage = wrappedText[i][n] as Canvas | Image;
|
const tempImage = wrappedText[i][n] as Canvas | Image;
|
||||||
const tempWidth = (textSize * tempImage.width) / tempImage.height; //等比例缩放到高度与字体大小相同后,图片宽度
|
const tempWidth = getInlineImageWidth(tempImage, textSize);
|
||||||
ctx.drawImage(
|
ctx.drawImage(
|
||||||
tempImage,
|
tempImage,
|
||||||
tempX,
|
tempX,
|
||||||
y - textSize / 3 - textSize / 2,
|
getInlineImageY(y, textSize),
|
||||||
tempWidth,
|
tempWidth,
|
||||||
textSize,
|
textSize,
|
||||||
);
|
);
|
||||||
@ -211,15 +217,14 @@ export function drawTextWithImages({
|
|||||||
* @param options1 - options1参数。
|
* @param options1 - options1参数。
|
||||||
*/
|
*/
|
||||||
function wrapTextWithImages({
|
function wrapTextWithImages({
|
||||||
textSize = 40,
|
textSize = BANGDREAM_TEXT_SPEC.font.defaultSize,
|
||||||
maxWidth,
|
maxWidth,
|
||||||
content,
|
content,
|
||||||
spacing = textSize / 3,
|
spacing = getTextInlineSpacing(textSize),
|
||||||
font = 'old',
|
font = 'old',
|
||||||
}: TextWithImagesOptions) {
|
}: TextWithImagesOptions) {
|
||||||
const canvas = new Canvas(1, 1);
|
const ctx = createMeasureContext();
|
||||||
const ctx = canvas.getContext('2d');
|
ctx.textBaseline = BANGDREAM_TEXT_SPEC.font.baseline;
|
||||||
ctx.textBaseline = 'alphabetic';
|
|
||||||
setFontStyle(ctx, textSize, font);
|
setFontStyle(ctx, textSize, font);
|
||||||
const temp: Array<Array<string | Image | Canvas>> = [[]];
|
const temp: Array<Array<string | Image | Canvas>> = [[]];
|
||||||
let lineNumber = 0;
|
let lineNumber = 0;
|
||||||
@ -274,7 +279,7 @@ function wrapTextWithImages({
|
|||||||
}
|
}
|
||||||
} else if (content[i] instanceof Canvas || content[i] instanceof Image) {
|
} else if (content[i] instanceof Canvas || content[i] instanceof Image) {
|
||||||
const tempImage = content[i] as Image;
|
const tempImage = content[i] as Image;
|
||||||
const tempWidth = tempImage.width * (textSize / tempImage.height);
|
const tempWidth = getInlineImageWidth(tempImage, textSize);
|
||||||
if (tempX + tempWidth > maxWidth) {
|
if (tempX + tempWidth > maxWidth) {
|
||||||
newLine();
|
newLine();
|
||||||
}
|
}
|
||||||
@ -309,3 +314,14 @@ export const setFontStyle = function (
|
|||||||
//设置字体大小
|
//设置字体大小
|
||||||
ctx.font = `${textSize}px ${font},${BANGDREAM_RENDER_THEME.font.fallback}`;
|
ctx.font = `${textSize}px ${font},${BANGDREAM_RENDER_THEME.font.fallback}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建文本测量上下文。
|
||||||
|
*/
|
||||||
|
function createMeasureContext() {
|
||||||
|
const canvas = new Canvas(
|
||||||
|
BANGDREAM_TEXT_SPEC.canvas.measureWidth,
|
||||||
|
BANGDREAM_TEXT_SPEC.canvas.measureHeight,
|
||||||
|
);
|
||||||
|
return canvas.getContext('2d');
|
||||||
|
}
|
||||||
|
|||||||
52
test/qqbot/plugins/bangDream/tsugu/text-spec.spec.ts
Normal file
52
test/qqbot/plugins/bangDream/tsugu/text-spec.spec.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {
|
||||||
|
BANGDREAM_TEXT_SPEC,
|
||||||
|
createTextCanvasSize,
|
||||||
|
getInlineImageWidth,
|
||||||
|
getInlineImageY,
|
||||||
|
getTextBaselineY,
|
||||||
|
getTextInlineSpacing,
|
||||||
|
getTextLineHeight,
|
||||||
|
} from '@/qqbot/plugins/bangDream/tsugu/canvas/text-spec';
|
||||||
|
|
||||||
|
describe('BangDream text spec', () => {
|
||||||
|
it('keeps text line and spacing ratios stable', () => {
|
||||||
|
expect(BANGDREAM_TEXT_SPEC.font.defaultSize).toBe(40);
|
||||||
|
expect(getTextLineHeight(30)).toBe(40);
|
||||||
|
expect(getTextInlineSpacing(30)).toBe(10);
|
||||||
|
expect(getTextBaselineY(60, 30)).toBe(40);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps inline image scaling stable', () => {
|
||||||
|
const image = { height: 20, width: 40 };
|
||||||
|
|
||||||
|
expect(getInlineImageWidth(image, 30)).toBe(60);
|
||||||
|
expect(getInlineImageY(40, 30)).toBe(15);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates canvas sizes with historical empty, single and multiline rules', () => {
|
||||||
|
expect(
|
||||||
|
createTextCanvasSize({
|
||||||
|
lineHeight: 50,
|
||||||
|
maxWidth: 200,
|
||||||
|
numberOfLines: 0,
|
||||||
|
}),
|
||||||
|
).toEqual({ height: 50, width: 1 });
|
||||||
|
|
||||||
|
expect(
|
||||||
|
createTextCanvasSize({
|
||||||
|
lineHeight: 50,
|
||||||
|
maxWidth: 200,
|
||||||
|
numberOfLines: 1,
|
||||||
|
singleLineWidth: 123,
|
||||||
|
}),
|
||||||
|
).toEqual({ height: 50, width: 123 });
|
||||||
|
|
||||||
|
expect(
|
||||||
|
createTextCanvasSize({
|
||||||
|
lineHeight: 50,
|
||||||
|
maxWidth: 200,
|
||||||
|
numberOfLines: 3,
|
||||||
|
}),
|
||||||
|
).toEqual({ height: 150, width: 200 });
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user