import { BaseDrawService } from '../../../core/draw/baseDraw'; import type { LetterTracingData, TracingRow, } from '../generators/letter-tracing-generator'; import { fontFamilyOf } from '../../shared/data/fontProfiles'; import { calcLetterFontSizes, drawFourLineGrid, drawLetterInFourLineGrid, FOUR_LINE_GRID_H, type LetterFontSizes, } from '../../shared/draw/drawTools'; const COLOR_BLACK = '#1a1a1a'; const COLOR_LIGHT_RED = '#FF8E8E'; /** 左右大写/小写两栏之间的间距(相对行宽 + 下限,避免过窄屏过小) */ const COLUMN_GUTTER_RATIO = 0.02; const COLUMN_GUTTER_MIN = 14; /** * 单栏内字母排版区域占栏宽比例(<1 则同栏字母更紧凑、字间距更小) */ const INTRA_COLUMN_LETTER_BAND_RATIO = 1; type CasePairingData = Extract< LetterTracingData, { mode: 'letter-tracing-case-pairing' } >; /** * 分栏对照绘制: * 四线三格横跨整页宽度,字母分左右两侧书写—— * 左栏大写、右栏小写(各 repetitions 个),中间留间距;同栏内字母略收紧。 * 每侧第一个字母黑色,后续浅红色。 */ export default class CasePairingDraw extends BaseDrawService { private letterSizes: LetterFontSizes = calcLetterFontSizes(FOUR_LINE_GRID_H); async draw(data: CasePairingData) { this.prepareDraw(); await this.drawHeaderAndDivider(); this.drawContent(data.leftRows, data.rightRows); this.drawPrintFooter(); } private drawContent(leftRows: TracingRow[], rightRows: TracingRow[]) { const M = 24; const lineW = this.canvasWidth - M * 2; const maxR = Math.max(leftRows.length, rightRows.length); const footerReserve = 56; const availableH = this.canvasHeight - this.currentY - footerReserve - 12; const rowGap = Math.max( 4, (availableH - maxR * FOUR_LINE_GRID_H) / (maxR + 1), ); const y0 = this.currentY + rowGap; const columnGutter = Math.max( COLUMN_GUTTER_MIN, Math.round(lineW * COLUMN_GUTTER_RATIO), ); const halfContentW = (lineW - columnGutter) / 2; const leftX = M; const rightX = M + halfContentW + columnGutter; for (let i = 0; i < maxR; i++) { const y = y0 + i * (FOUR_LINE_GRID_H + rowGap); drawFourLineGrid(this.ctx, M, y, lineW, FOUR_LINE_GRID_H); if (i < leftRows.length) { this.drawLettersInHalf( leftRows[i], leftX, y, halfContentW, INTRA_COLUMN_LETTER_BAND_RATIO, ); } if (i < rightRows.length) { this.drawLettersInHalf( rightRows[i], rightX, y, halfContentW, INTRA_COLUMN_LETTER_BAND_RATIO, ); } } } /** * 在四线三格的半侧区域内绘制字母。 * 第一个字母黑色,后续字母浅红色。 */ private drawLettersInHalf( row: TracingRow, x: number, y: number, columnW: number, letterBandRatio: number, ) { const n = row.cells.length; if (n === 0) return; const bandW = columnW * letterBandRatio; const inset = (columnW - bandW) / 2; const slotW = bandW / n; for (let i = 0; i < n; i++) { const cell = row.cells[i]; const color = i === 0 ? COLOR_BLACK : COLOR_LIGHT_RED; const cx = x + inset + slotW * i + slotW / 2; drawLetterInFourLineGrid( this.ctx, cell.char, cx, y, FOUR_LINE_GRID_H, this.letterSizes, { fontFamily: fontFamilyOf(this.letterSizes._profile), color }, ); } } }