import { BaseDrawService } from '../../../core/draw/baseDraw'; import type { LetterTracingData, SingleLineRow, } from '../generators/letter-tracing-generator'; import { fontFamilyOf, PRINT_CLEARLY_DASHED, } from '../../../core/font/fontProfiles'; import { calcLetterFontSizes, drawFourLineGrid, drawLetterInFourLineGrid, FOUR_LINE_GRID_H, loadLetterFont, type LetterFontSizes, } from '../../shared/draw/drawTools'; const SLOTS_PER_ROW = 7; const PAIR_GAP = 25; type SingleLineData = Extract< LetterTracingData, { mode: 'letter-tracing-half' } >; /** * 单字母逐行绘制: * 13 行四线三格横跨整页宽度,每行按 7 组位置预留空间。 * 前 4 组展示大小写字母(Aa),其余位置留空供用户书写。 * 颜色递进:黑色 → 浅红 → 半透明浅红 → 虚线字体+半透明浅红。 */ export default class HalfLetterDraw extends BaseDrawService { private letterSizes: LetterFontSizes = calcLetterFontSizes(FOUR_LINE_GRID_H); private dashedSizes: LetterFontSizes = calcLetterFontSizes( FOUR_LINE_GRID_H, PRINT_CLEARLY_DASHED, ); async draw(data: SingleLineData) { this.prepareDraw(); await Promise.all([ this.drawHeaderAndDivider(), loadLetterFont(PRINT_CLEARLY_DASHED), ]); this.drawContent(data.rows); this.drawPrintFooter(); } private drawContent(rows: SingleLineRow[]) { const M = 24; const lineW = this.canvasWidth - M * 2; const rowCount = rows.length; const footerReserve = 56; const availableH = this.canvasHeight - this.currentY - footerReserve - 12; const rowGap = Math.max( 4, (availableH - rowCount * FOUR_LINE_GRID_H) / (rowCount + 1), ); const y0 = this.currentY + rowGap; const slotW = lineW / SLOTS_PER_ROW; for (let i = 0; i < rowCount; i++) { const y = y0 + i * (FOUR_LINE_GRID_H + rowGap); drawFourLineGrid(this.ctx, M, y, lineW, FOUR_LINE_GRID_H); const row = rows[i]; for (let j = 0; j < row.cells.length; j++) { const cell = row.cells[j]; if (!cell) continue; const pairCx = M + j * slotW + slotW / 2; const isDashed = cell.style === 'dashed'; const sizes = isDashed ? this.dashedSizes : this.letterSizes; const fontFamily = fontFamilyOf( isDashed ? PRINT_CLEARLY_DASHED : sizes._profile, ); this.ctx.save(); this.ctx.globalAlpha = cell.alpha; drawLetterInFourLineGrid( this.ctx, cell.upper, pairCx - PAIR_GAP / 2, y, FOUR_LINE_GRID_H, sizes, { fontFamily, color: cell.color }, ); drawLetterInFourLineGrid( this.ctx, cell.lower, pairCx + PAIR_GAP / 2, y, FOUR_LINE_GRID_H, sizes, { fontFamily, color: cell.color }, ); this.ctx.restore(); } } } }