static measure()

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
  }