Files
2026-05-19 18:24:43 +08:00

132 lines
3.8 KiB
TypeScript

import { BaseDrawService } from '../../../core/draw/baseDraw';
import type { LetterTracingData } from '../generators/letter-tracing-generator';
import {
PRINT_CLEARLY_BOLD,
PRINT_CLEARLY_DASHED,
fontFamilyOf,
} from '../../../core/font/fontProfiles';
import {
calcLetterFontSizes,
drawFourLineGrid,
drawLetterInFourLineGrid,
FOUR_LINE_GRID_H,
loadLetterFont,
type LetterFontSizes,
} from '../../shared/draw/drawTools';
import { TRACING_COLORS } from '../../../core/data/tracingStyles';
type UpperLowerData = Extract<
LetterTracingData,
{ mode: 'letter-tracing-upper-lower' }
>;
const M = 24;
const SECTION_GAP = 25;
const TITLE_SIZE = 28;
const TITLE_GAP = 25;
const ROW_GAP = 24;
const TITLE_COLOR = '#000';
const TRACE_LIGHT_RED = TRACING_COLORS.guide;
const DASHED_PROFILE = PRINT_CLEARLY_DASHED;
const TITLE_PROFILE = PRINT_CLEARLY_BOLD;
const LETTER_FACE = fontFamilyOf(DASHED_PROFILE);
const TITLE_FACE = fontFamilyOf(TITLE_PROFILE);
/**
* 字母总览:上区大写、下区小写;每区标题 + 4 行四线三格(7+7+6+6)。
* 列宽按 7 列均分;后两行 6 字左对齐,与前两行逐列对齐(占前 6 列)。
*/
export default class UpperLowerDraw extends BaseDrawService {
private letterSizes: LetterFontSizes = calcLetterFontSizes(
FOUR_LINE_GRID_H,
DASHED_PROFILE,
);
async draw(data: UpperLowerData) {
this.prepareDraw();
await Promise.allSettled([
loadLetterFont(DASHED_PROFILE),
loadLetterFont(TITLE_PROFILE),
]);
await this.drawHeaderAndDivider();
this.drawUpperLowerBody(data.upperRows, data.lowerRows);
await this.drawPrintFooter();
}
private drawUpperLowerBody(
upperRows: string[][],
lowerRows: string[][],
): void {
const lineW = this.canvasWidth - M * 2;
const slotW = lineW / 7;
let y = this.currentY + SECTION_GAP - 7;
y = this.drawLetterBlock(
'Uppercase Letters',
upperRows,
y,
lineW,
slotW,
);
y += SECTION_GAP + 7;
this.drawLetterBlock('lowercase letters', lowerRows, y, lineW, slotW);
}
/**
* 绘制一个分区:居中标题 + 4 行四线三格与字母。
* @returns 该分区最后一行四线三格底边 Y
*/
private drawLetterBlock(
title: string,
rows: string[][],
startY: number,
lineW: number,
slotW: number,
): number {
const ctx = this.ctx;
const centerX = M + lineW / 2;
ctx.save();
ctx.font = `bold ${TITLE_SIZE}px ${TITLE_FACE}`;
ctx.fillStyle = TITLE_COLOR;
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText(title, centerX, startY);
ctx.restore();
let y = startY + TITLE_SIZE + TITLE_GAP;
for (let r = 0; r < rows.length; r++) {
const chars = rows[r];
drawFourLineGrid(ctx, M, y, lineW, FOUR_LINE_GRID_H);
/** 与 7 字行共用列宽 slotW,后两行 6 字左对齐(占前 6 列),与上行逐列对齐 */
const offsetX = M;
for (let i = 0; i < chars.length; i++) {
const cx = offsetX + slotW * (i + 0.5);
drawLetterInFourLineGrid(
ctx,
chars[i],
cx,
y,
FOUR_LINE_GRID_H,
this.letterSizes,
{
fontFamily: LETTER_FACE,
color: TRACE_LIGHT_RED,
},
);
}
y += FOUR_LINE_GRID_H;
if (r < rows.length - 1) {
y += ROW_GAP;
}
}
return y;
}
}