in packages/core/src/text/font-metrics.ts [59:155]
static measure(cssFontProp: string, options?: Partial<MeasureOptions>): FontMetrics {
if (fontMetricsCache[cssFontProp]) {
return fontMetricsCache[cssFontProp]
}
const {
canvas,
context,
metricsString,
baselineSymbol,
baselineMultiplier,
heightMultiplier,
} = options
? { ...FontMetrics.defaultMeasureOptions, ...options }
: FontMetrics.defaultMeasureOptions
const fontProps = FontProps.from(cssFontProp)
FontProps.validateLengthUnit(fontProps['font-size'], 'px')
const fontMetrics: FontMetrics = {
ascent: 0,
descent: 0,
fontSize: 0,
cssFontProp,
}
// 定位 baseline, 测量画布宽高
context.font = cssFontProp
const baseline = Math.ceil(context.measureText(baselineSymbol).width * baselineMultiplier)
const canvasWidth = Math.ceil(context.measureText(metricsString).width)
const canvasHeight = Math.ceil(heightMultiplier * baseline)
// 应用画布宽高
canvas.width = canvasWidth
canvas.height = canvasHeight
// 填充背景色
context.fillStyle = '#f00'
context.fillRect(0, 0, canvasWidth, canvasHeight)
// 绘制测量用文本
context.font = cssFontProp
context.textRendering = 'geometricPrecision'
context.textBaseline = 'alphabetic'
context.fillStyle = '#000'
context.fillText(metricsString, 0, baseline)
// 得到像素信息
const pixels = context.getImageData(0, 0, canvasWidth, canvasHeight).data
const totalPixelCount = pixels.length
const rowPixelCount = canvasWidth * 4
let i = 0
let cursor = 0
let stop = false
// 探测 `ascent`: 从上到下扫描,直到遇到不是红色的色值
for (i = 0; i < baseline; ++i) {
for (let j = 0; j < rowPixelCount; j += 4) {
if (pixels[cursor + j] !== 255) {
stop = true
break
}
}
if (!stop) {
cursor += rowPixelCount
} else {
break
}
}
fontMetrics.ascent = baseline - i
cursor = totalPixelCount - rowPixelCount
stop = false
// 探测 `descent`: 从下到上扫描,直到遇到不是红色的色值
for (i = canvasHeight; i > baseline; --i) {
for (let j = 0; j < rowPixelCount; j += 4) {
if (pixels[cursor + j] !== 255) {
stop = true
break
}
}
if (!stop) {
cursor -= rowPixelCount
} else {
break
}
}
fontMetrics.descent = i - baseline
fontMetrics.fontSize = fontMetrics.ascent + fontMetrics.descent
return fontMetricsCache[cssFontProp] = fontMetrics
}