HarmonyOS鸿蒙Next中注册自定义字体,Text渲染和Canvas绘制时缺失部分字体(没有绘制)

HarmonyOS鸿蒙Next中注册自定义字体,Text渲染和Canvas绘制时缺失部分字体(没有绘制)

功能描述

当前有个场景,自定义字体可能是本地的,也可能是云测下载下来的,需要使用Canvas去绘制或Text去渲染。

问题

当前我在注册字体成功后,进入页面,Canvas在绘制或Text渲染文章时,某些字或者所有文字可能缺失,视觉效果上看不到字。(重启应用后恢复正常, 简单的销毁页面重建则无法恢复)

注册字体在进入页面之前的,且注册成功后,才放行进入到对应界面,切进入页面后又加了120ms的延时渲染,但还是有概率出现此问题。

版本信息

cke_23307.png


更多关于HarmonyOS鸿蒙Next中注册自定义字体,Text渲染和Canvas绘制时缺失部分字体(没有绘制)的实战教程也可以访问 https://www.itying.com/category-93-b0.html

11 回复

您好,未复现问题,可以提供复现问题的可运行最小化demo嘛,或者您可以参考以下 Canvas绘制自定义字体可以调用字体引擎的fontCollection.loadFontSync接口来注册自定义字体,示例可以参照官网font属性

import { text } from '@kit.ArkGraphics2D';

@Entry
@Component
struct FontDemo {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Canvas(this.context)
        .width('100%')
        .height('100%')
        .backgroundColor('rgb(213,213,213)')
        .onReady(() => {
          // 加载rawfile目录下的自定义字体文件myFont.ttf
          let fontCollection = text.FontCollection.getGlobalInstance();
          fontCollection.loadFontSync('HarmonyOS_Sans_Thin_Italic', $rawfile("myFont.ttf"))
          // 加粗,字体大小为30vp,字体系列为HarmonyOS_Sans_Thin_Italic
          this.context.font = "bold 30vp HarmonyOS_Sans_Thin_Italic"
          this.context.fillText("Hello customFont", 20, 140)
        })
    }
    .width('100%')
    .height('100%')
  }
}

更多关于HarmonyOS鸿蒙Next中注册自定义字体,Text渲染和Canvas绘制时缺失部分字体(没有绘制)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


我调用的就是这个,我们这边自定义字体比较多,它是有概率出现的,我们这边基本上20个复现一个:cke_284.png

它这个不止Canvas有问题,Text使用也有一样的问题

您好,本地尝试未复现,麻烦您提供以下信息以便进行排查

可复现问题的完整Demo以及IDE和设备版本号

补充一个定位思路:既然 Text 也会缺字,先别把问题只归到 Canvas。ctx.font 只有在字体已经被字体引擎注册且 family 名一致时才会命中自定义字体;API20+ 才明确支持注册字体。建议入口处只加载一次全局 FontCollection,绘制前用同一个 family 预热,再检测是否是字体包本身缺 glyph。

import { text } from '@kit.ArkGraphics2D'

const fc = text.FontCollection.getGlobalInstance()

fc.loadFontSync('BizFont','rawfile/font/biz.ttf')

Canvas(this.ctx).onReady(() => {
  this.ctx.font = '28px BizFont'
  this.ctx.fillText('待验证文字', 20, 40)
})

排查:1. BizFont 与 ctx.font 完全一致;2. 真机验证,预览器不支持自定义字体;3. 开启 setTextUndefinedGlyphDisplay(USE_TOFU),若缺字变方块就是字体子集缺字形。参考文档:CanvasRenderingContext2D.font、FontCollection.loadFontSync、@ohos.font 注册自定义字体。

会不会是文件名中有空格或者其他特殊字符?

loadFontSync(name: string, path: string | Resource): void

path: 需要导入的字体文件的路径,应为 “file:// + 字体文件绝对路径” 或 “rawfile/目录or文件名”。

import { text } from '@kit.ArkGraphics2D'

let fontCollection: text.FontCollection = new text.FontCollection();

@Entry
@Component
struct RenderTest {
  LoadFontSyncTest() {
    fontCollection.loadFontSync('Clock_01', 'file:///system/fonts/HarmonyClock_01.ttf')
    let fontFamilies: Array<string> = ["Clock_01"]
    let myTextStyle: text.TextStyle = {
      fontFamilies: fontFamilies
    };
    let myParagraphStyle: text.ParagraphStyle = {
      textStyle: myTextStyle,
    }
    let paragraphBuilder: text.ParagraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);

    let textData = "测试 loadFontSync 加载字体HarmonyClock_01.ttf";
    paragraphBuilder.addText(textData);
    let paragraph: text.Paragraph = paragraphBuilder.build();
    paragraph.layoutSync(600);
  }

  aboutToAppear() {
    this.LoadFontSyncTest();
  }

  build() {
  }
}

跟这个没关系

这个问题从现象来看,不像是字体注册失败,而更像是字体注册完成后,Canvas 底层字体缓存(Font Manager)还没有完全就绪,导致部分 Glyph(字形)偶发丢失

你描述的几个关键信息:

✓ registerFont 成功
✓ 页面进入前已完成注册
✓ 进入页面后又延迟120ms
✗ Canvas绘制时偶发缺字
✗ 同一个字体有时正常有时缺失

这种情况我更倾向于下面几个方向。


第一种可能(概率最高)

字体注册成功 ≠ 字体加载完成

很多字体系统实际上是:

registerFont
↓
加入字体管理器
↓
异步解析TTF/OTF
↓
建立Glyph缓存
↓
首次Raster

而Canvas的:

ctx.fillText(...)

可能在:

Glyph Cache建立前

就开始绘制了。

于是出现:

汉字A 正常
汉字B 缺失
汉字C 正常

因为:

A 已缓存
B 未缓存

这种现象在Canvas体系里非常常见。


第二种可能

下载字体后立即注册

你提到:

字体可能来自云端下载

重点检查:

download()
↓
writeFile()
↓
registerFont()

之间有没有:

fsync
flush
close

真正完成。

很多时候:

文件已存在
≠
文件已完全写入

尤其大字体:

思源黑体
HarmonyOS Sans
阿里巴巴普惠体

10~30MB很常见。

可能出现:

注册时读取到了不完整文件

导致部分字形表异常。


第三种可能

字体本身缺Glyph

这个要重点排查。

很多设计字体:

只支持:
ASCII
数字
常用汉字

不支持:

扩展汉字
生僻字
特殊符号
Emoji

例如:

𠮷
㵘
㎏
①
™
®

Canvas不会自动字体回退时,就会出现:

整个字符消失

而不是显示方块。

建议打印:

text.codePointAt(i)

看看缺失的是不是固定字符。


第四种可能

Canvas字体缓存Bug

目前你用的是:

HarmonyOS 6.1.1
API24

从版本来看:

OpenHarmony SDK
6.1.1.125

已经属于较新的SDK。

但Canvas文字渲染底层是:

ArkUI
↓
Skia
↓
FontManager

如果:

首次使用动态注册字体

可能存在:

FontCache未刷新

情况。

表现就是:

第一次缺字
第二次正常

建议验证

验证1

注册后先用Text组件渲染一次

Text("测试字体加载")
  .fontFamily("MyFont")
  .opacity(0.01)

不要直接Canvas。

流程:

registerFont
↓
Text先渲染
↓
等待一帧
↓
Canvas绘制

很多字体缓存问题会直接消失。


验证2

首次绘制两次

ctx.fillText(text, x, y)

setTimeout(() => {
    ctx.clearRect(...)
    ctx.fillText(text, x, y)
}, 50)

如果:

第一次缺
第二次正常

基本可以确认:

Font Cache问题

验证3

统计缺失字符

例如:

const text = "中华人民共和国"

for (let i = 0; i < text.length; i++) {
  console.info(text[i])
}

记录:

哪一个字缺失

观察:

总是同一个字缺
还是随机缺

如果总是同一个字

大概率:

字体文件问题

如果随机缺

大概率:

Canvas字体缓存问题

第五种可能

Canvas比Text更容易触发问题

很多开发者容易忽略:

Text组件

Canvas.fillText()

底层并不是完全同一套流程。

经常出现:

Text正常
Canvas缺字

或者:

Text支持字体回退
Canvas不支持

这种情况。

建议先验证:

Text()
  .fontFamily(你的字体)

是否100%正常。

如果:

Text正常
Canvas异常

基本可以认定是Canvas渲染链路问题,而不是字体文件问题。


结合你的描述:

注册成功
延迟120ms
偶发缺字
本地字体和下载字体都能复现

我认为最可能的原因排序是:

① Canvas字体缓存未完成初始化(70%)

② Canvas动态字体渲染Bug(20%)

③ 字体文件Glyph缺失(8%)

④ 下载文件未完全落盘(2%)

如果这是准备向华为提单的问题,建议附上:

  1. 最小复现Demo
  2. 字体文件(ttf/otf)
  3. 缺失字符具体内容
  4. Text组件是否正常
  5. 第二次fillText是否恢复

这样研发基本能直接定位到是 ArkUI Canvas 字体缓存链路还是 FontManager 的问题。

同样的逻辑,同样的代码,我抽成小demo后反而复现不出来

在HarmonyOS Next中注册自定义字体后,Text渲染和Canvas绘制缺失部分字符,通常因字体文件本身不包含这些字符的glyph,或注册时fontFamily名称与使用时不匹配。确保自定义字体文件为TTF/OTF格式且字符集完整,调用registerFont时fontFamily参数与Text或Canvas的font属性设置一致。若仍缺失,检查字体文件是否损坏。

字体注册虽然是同步调用,但系统字体的缓存刷新可能存在延迟,导致页面创建时字体尚未完全就绪,而重启应用会重新加载所有资源,因此表现正常。
可尝试以下方式解决:

  1. 注册提前:将字体注册放在 AbilityonCreateonWindowStageCreate 中,确保在页面渲染前已完成。
  2. 条件渲染:在注册成功后(可借助 fonts.isFontLoaded 二次确认),再动态创建或显示使用自定义字体的 Text/Canvas 组件,避免组件在字体未就绪时已构建。
  3. Canvas 重绘:Canvas 绘制操作前显式调用 canvas.font = '...' 重新指定字体,并在字体 ready 后通过状态变化触发重绘,而非仅依赖延时。
  4. 版本问题:若以上均无效,可能是系统早期版本的缺陷,请升级到最新的 HarmonyOS Next SDK 并重试。

核心在于保证字体完全加载后再开始绑定和绘制,避免组件实例化与字体缓存刷新的竞态。

回到顶部