ArKTS: font

 

/**
 # encoding: utf-8
 # 版权所有  2025 ©涂聚文有限公司™ ®
 # 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 # 描述:
 # Author    : geovindu,Geovin Du 涂聚文.
 # IDE       : DevEco Studio 5.1.1  HarmonyOS ArKTS
 # os        : windows 10
 # database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
 # Datetime  : 2025/8/9 5:22
 # User      :  geovindu
 # Product   : DevEco Studio
 # Project   : sqliteAppHelper
 # File      : DuNodeController.ets
 **/
import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
import { UIContext } from '@kit.ArkUI'
import { text } from '@kit.ArkGraphics2D'

/**
 * 自定义节点控制器类
 */
export  class DuNodeController extends NodeController {
  private rootNode: FrameNode | null = null;
  makeNode(uiContext: UIContext): FrameNode {
    this.rootNode = new FrameNode(uiContext)
    if (this.rootNode == null) {
      return this.rootNode
    }
    const renderNode = this.rootNode.getRenderNode()
    if (renderNode != null) {
      renderNode.frame = { x: 0, y: 0, width: 300, height: 50 }
      renderNode.pivot = { x: 0, y: 0 }
    }
    return this.rootNode
  }
  addNode(node: RenderNode): void {
    if (this.rootNode == null) {
      return
    }
    const renderNode = this.rootNode.getRenderNode()
    if (renderNode != null) {
      renderNode.appendChild(node)
      console.log('节点已添加到控制器');
    }
  }
  clearNodes(): void {
    if (this.rootNode == null) {
      return
    }
    const renderNode = this.rootNode.getRenderNode()
    if (renderNode != null) {
      renderNode.clearChildren()
    }
  }
}

/**
 # encoding: utf-8
 # 版权所有  2025 ©涂聚文有限公司™ ®
 # 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 # 描述:
 # Author    : geovindu,Geovin Du 涂聚文.
 # IDE       : DevEco Studio 5.1.1  HarmonyOS ArKTS
 # os        : windows 10
 # database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
 # Datetime  : 2025/8/9 5:19
 # User      :  geovindu ArkGraphics2D
 # Product   : DevEco Studio
 # Project   : sqliteAppHelper
 # File      : DuRenderNode.ets
 **/
import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
import { UIContext } from '@kit.ArkUI'
import { text } from '@kit.ArkGraphics2D'

/**
 * 创建一个自定义的渲染节点类,用于绘制文本
 */
export  class DuRenderNode extends RenderNode {
  async draw(context: DrawContext) {
    // 创建画布canvas对象
    const canvas = context.canvas
    // 获取全局字体集实例
    let fontCollection = text.FontCollection.getGlobalInstance() //获取Arkui全局FC
    // 注册自定义字体
    fontCollection.loadFontSync('myFamilyName', 'file:///system/fonts/NotoSansMalayalamUI-SemiBold.ttf')
    // 方式二:确保已经将自定义字体myFontFile.ttf文件放在本应用工程的entry/src/main/resources/rawfile目录
    fontCollection.loadFontSync('mao',$rawfile('mao.ttf'))
    // 使用自定义字体
    let myFontFamily: Array<string> = ["mao"] // 如果已经注册自定义字体,填入自定义字体的字体家族名
    // 设置文本样式
    let myTextStyle: text.TextStyle = {
      color: { alpha: 255, red: 255, green: 0, blue: 0 },
      fontSize: 80,
      // 在文本样式中加入可使用的自定义字体
      fontFamilies: myFontFamily
    };
    // 创建一个段落样式对象,以设置排版风格
    let myParagraphStyle: text.ParagraphStyle = {
      textStyle: myTextStyle,
      align: 3,
      wordBreak:text.WordBreak.NORMAL
    };
    // 创建一个段落生成器
    let ParagraphGraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection)
    // 在段落生成器中设置文本样式
    ParagraphGraphBuilder.pushStyle(myTextStyle);
    // 在段落生成器中设置文本内容
    ParagraphGraphBuilder.addText("《沁园春·雪》\n" +
      "\n" +
      "作者:毛\n北国风光,千里冰封,万里雪飘。\n" +
      "\n" +
      "望长城内外,惟余莽莽;大河上下,顿失滔滔。\n" +
      "\n" +
      "山舞银蛇,原驰蜡象,欲与天公试比高。\n" +
      "\n" +
      "须晴日,看红装素裹,分外妖娆。\n" +
      "\n" +
      "江山如此多娇,引无数英雄竞折腰。\n" +
      "\n" +
      "惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。\n" +
      "\n" +
      "一代天骄,成吉思汗,只识弯弓射大雕。\n" +
      "\n" +
      "俱往矣,数风流人物,还看今朝。");
    // 通过段落生成器生成段落
    let paragraph = ParagraphGraphBuilder.build();
    // 设置段落宽度为1000px
    paragraph.layoutSync(1000);
    paragraph.paint(canvas, 0, 400);
  }
}

  

/**
 # encoding: utf-8
 # 版权所有  2025 ©涂聚文有限公司™ ®
 # 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 # 描述: 自定义字体页面
 # Author    : geovindu,Geovin Du 涂聚文.
 # IDE       : DevEco Studio 5.1.1  HarmonyOS ArKTS
 # os        : windows 10
 # database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
 # Datetime  : 2025/8/9 5:19
 # User      :  geovindu
 # Product   : DevEco Studio
 # Project   : sqliteAppHelper
 # File      : CustomFontPage.ets
 **/

import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
import { UIContext } from '@kit.ArkUI'
import { text } from '@kit.ArkGraphics2D'
import { DuNodeController } from "../utils/DuNodeController"
import { DuRenderNode } from "../utils/DuRenderNode"


// 创建并初始化渲染节点实例
const newNode = new DuRenderNode();
// 设置渲染节点的位置和尺寸
newNode.frame = { x: 0, y: 0, width: 400, height: 600 };

@Entry
@Component
struct MaoFontntPage {
  @State message: string = 'Hello Geovin Dui';


  private Controller: DuNodeController = new DuNodeController()
  build() {
    Scroll()
    {
      Column() {
        Row() {
          NodeContainer(this.Controller)
            .height('100%')
        }
        .height('99%')
        .backgroundColor(Color.White)
        Row(){
          Button("写字")
            .fontSize('16fp')
            .fontWeight(500)
            .margin({ bottom: 24, right: 12 })
            .onClick(() => {
              this.Controller.clearNodes()
              this.Controller.addNode(newNode)
            })
            .width('50%')
            .height(40)
            .shadow(ShadowStyle.OUTER_DEFAULT_LG)
        }
        .width('100%')
        .justifyContent(FlexAlign.Center) // 设置当前Row容器内子元素在主轴上居中对齐
        .shadow(ShadowStyle.OUTER_DEFAULT_SM) // 设置Row容器外阴影效果
        .alignItems(VerticalAlign.Bottom) // 设置当前Row容器内子元素在交叉轴(垂直方向)上的对齐方式为底部对齐
        .layoutWeight(1) // 设置当前Row在父容器Column中的布局权重为1

      }
    }

  }
}

  

/**
 # encoding: utf-8
 # 版权所有  2025 ©涂聚文有限公司™ ®
 # 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 # 描述: 自定义字体页面
 # Author    : geovindu,Geovin Du 涂聚文.
 # IDE       : DevEco Studio 5.1.1  HarmonyOS ArKTS
 # os        : windows 10
 # database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
 # Datetime  : 2025/8/9 5:19
 # User      :  geovindu
 # Product   : DevEco Studio
 # Project   : sqliteAppHelper
 # File      : CustomFontPage.ets
 **/

import { NodeController, FrameNode, RenderNode, DrawContext } from '@kit.ArkUI'
import { UIContext } from '@kit.ArkUI'
import { text } from '@kit.ArkGraphics2D'
import { DuNodeController } from "../utils/DuNodeController"
import { DuRenderNode } from "../utils/DuRenderNode"
import font from '@ohos.font';

// 创建并初始化渲染节点实例
const newNode = new DuRenderNode();
// 设置渲染节点的位置和尺寸
newNode.frame = { x: 0, y: 0, width: 400, height: 600 };

@Entry
@Component
struct MaoFontntPage {
  @State message: string = 'Hello Geovin Dui';

  aboutToAppear() {
    // familyName和familySrc都支持string
    font.registerFont({
      familyName: 'maozedong',
      familySrc: 'font/maozedong.ttf',// font文件与pages目录同级
    })
  }


  private Controller: DuNodeController = new DuNodeController()
  build() {
    Scroll()
    {
      Column() {
        Row() {
          NodeContainer(this.Controller)
            .height('100%')
        }
        .height('99%')
        .backgroundColor(Color.White)
        Row(){
          Button("写字")
            .fontSize('16fp')
            .fontWeight(500)
            .fontFamily('maozedong')
            .margin({ bottom: 24, right: 12 })
            .onClick(() => {
              this.Controller.clearNodes()
              this.Controller.addNode(newNode)
            })
            .width('50%')
            .height(40)
            .shadow(ShadowStyle.OUTER_DEFAULT_LG)
        }
        .width('100%')
        .justifyContent(FlexAlign.Center) // 设置当前Row容器内子元素在主轴上居中对齐
        .shadow(ShadowStyle.OUTER_DEFAULT_SM) // 设置Row容器外阴影效果
        .alignItems(VerticalAlign.Bottom) // 设置当前Row容器内子元素在交叉轴(垂直方向)上的对齐方式为底部对齐
        .layoutWeight(1) // 设置当前Row在父容器Column中的布局权重为1

      }
    }

  }
}

  

项目结构:

image

 

1754753068853

 

 

073b2e01896f15f9c3e854fcf8b5416

 

 

/**
 # encoding: utf-8
 # 版权所有  2025 ©涂聚文有限公司™ ®
 # 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 # 描述: 自定义字体页面  https://developer.huawei.com/consumer/en/doc/harmonyos-references-V14/ts-canvasrenderingcontext2d-V14#font
 # Author    : geovindu,Geovin Du 涂聚文.
 # IDE       : DevEco Studio 5.1.1  HarmonyOS ArKTS
 # os        : windows 10
 # database  : mysql 9.0 sql server 2019, postgreSQL 17.0  Oracle 21c Neo4j
 # Datetime  : 2025/8/9 5:19
 # User      :  geovindu
 # Product   : DevEco Studio
 # Project   : sqliteAppHelper
 # File      : CustomFontPage.ets
 **/

import promptAction from '@ohos.promptAction'
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';
import { text } from '@kit.ArkGraphics2D';
import { AddFormMenuItem } from '@kit.ArkUI';
import { formBindingData } from '@kit.FormKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import font from '@ohos.font';

class TextTyper {
  private fullText: string = "《沁园春·雪》\n\n作者:毛\n北国风光,千里冰封,万里雪飘。\n\n望长城内外,惟余莽莽;大河上下,顿失滔滔。\n\n山舞银蛇,原驰蜡象,欲与天公试比高。\n\n须晴日,看红装素裹,分外妖娆。\n\n江山如此多娇,引无数英雄竞折腰。\n\n惜秦皇汉武,略输文采;唐宗宋祖,稍逊风骚。\n\n一代天骄,成吉思汗,只识弯弓射大雕。\n\n俱往矣,数风流人物,还看今朝。";

  currentText: string = "";
  currentIndex: number = 0;
  typingSpeed: number = 100;
  timerId: number | null = null;
  fontCollection: text.FontCollection;
  fontLoaded: boolean = false;
  fontName: string = 'maozedong';
  fontPath: string = 'maozedong.ttf';


  // 新增:字体文件信息校验
  fontFileSize: number = 0; // 用于验证文件是否有效

  constructor() {
    this.fontCollection = text.FontCollection.getGlobalInstance();
    this.checkFontFileExists(); // 先检查文件是否可访问
    this.loadCustomFont();
  }

  // 关键:检查字体文件是否真的可访问
  private async checkFontFileExists() {
    try {
      const context = getContext(this) as common.UIAbilityContext;
      // 尝试读取文件元数据判断是否存在
      const fileInfo = context.resourceManager.getRawFile(this.fontPath);

      console.log(`[文件检查] 存在: ${this.fontPath}, 大小: ${this.fontFileSize}字节`);
      if (this.fontFileSize < 1024) { // 小于1KB的字体文件几乎肯定无效
        promptAction.showToast({ message: `警告:字体文件过小,可能损坏` });
      }
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[文件检查] 不存在或无法访问: ${this.fontPath}, 错误: ${err.code}`);
      promptAction.showToast({ message: `文件访问失败: ${err.code}` });
    }
  }

  // 优化的字体加载逻辑,增加格式兼容性处理
  private async loadCustomFont() {
    // 先确保文件存在且有效
    if (this.fontFileSize === 0) {
      console.log(`[字体加载] 跳过加载,文件无效`);
      return;
    }

    try {
      console.log(`[字体加载] 开始加载有效文件 (${this.fontFileSize}字节)`);

      // 尝试不同的字体加载方式
      const loadResult = await this.fontCollection.loadFont(
        this.fontName,
        $rawfile(this.fontPath),
      ).then(
        () => {
          console.log(`[字体加载] 加载成功`);
          return true;
        },
        (error:BusinessError) => {
          console.error(`[字体加载] 加载失败: ${error}`);
          return false;
        }
      );

      this.fontLoaded = loadResult;
      console.log(`[字体加载] 加载结果: ${this.fontLoaded}`);

      // 加载成功但可能未生效的额外提示
      if (this.fontLoaded) {
        promptAction.showToast({
          message: `字体加载成功,若显示异常请尝试更换TTF文件`
        });
      } else {
        promptAction.showToast({ message: `字体格式不兼容,请使用标准TTF` });
      }
    } catch (error) {
      this.fontLoaded = false;
      const err = error as BusinessError;
      console.error(`[字体加载] 异常: ${err.code}-${err.message}`);

      // 常见错误码提示
      const errorTips: Record<number, string> = {
        13900002: "文件格式错误(非TTF)",
        13900003: "文件损坏或加密",
        13900004: "不支持的字体格式"
      };
      const tip = errorTips[err.code] || `错误码: ${err.code}`;
      promptAction.showToast({ message: `加载失败: ${tip}` });
    }
  }

  startTyping(invalidate: () => void) {
    this.currentText = "";
    this.currentIndex = 0;

    if (this.timerId) clearInterval(this.timerId);

    this.timerId = setInterval(() => {
      if (this.currentIndex < this.fullText.length) {
        this.currentText += this.fullText[this.currentIndex];
        this.currentIndex++;
        invalidate();
      } else {
        clearInterval(this.timerId!);
        this.timerId = null;
      }
    }, this.typingSpeed);
  }

  resetTyping() {
    if (this.timerId) clearInterval(this.timerId);
    this.currentText = "";
    this.currentIndex = 0;
  }
}

@Entry
@Component
struct CustomFontPage {
  private settings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  @State needRedraw: boolean = false;
  private typer = new TextTyper();

  private canvasWidth: number = 600;
  private canvasHeight: number = 800;
  private fontColor: string = '#8B0000';
  private fontSize: number = 46;
  private lineHeight: number = 20;

  // 测试文本,用于直观对比字体效果
  private testText: string = "测试文本:毛字体";

  private draw() {
    if (!this.context) return;

    // 清空画布
    this.context.fillStyle = '#ffffff';
    this.context.fillRect(0, 0, this.canvasWidth, this.canvasHeight);

    // 构建字体样式SimHei
    const targetFont = this.typer.fontLoaded
      ? `${this.typer.fontName}`
      : `${this.typer.fontName}`;
    const fontStyle = `${this.fontSize}px ${this.typer.fontName}`; //, Microsoft YaHei, sans-serif
    this.context.font = fontStyle;

    // 字体生效检测
    this.context.fillStyle = '#666666';
    this.context.fillText(`当前字体配置: ${targetFont}`, 10, 40);

    // 显示测试文本对比
    this.context.fillStyle = '#0000ff';
    this.context.fillText(this.testText, 10, 80);
    this.context.font = `${this.fontSize}px ${this.typer.fontName}`; // 系统字体对比
    this.context.fillStyle = '#008000';
    this.context.fillText(`系统字体对比: ${this.testText}`, 10, 120);

    // 恢复设置绘制主文本
    this.context.font = fontStyle;
    this.context.fillStyle = this.fontColor;
    this.drawCenteredText(this.typer.currentText);
  }

  // 显示字体文件状态信息
  private showFontFileInfo() {
    return ""
  }

  private calculateTextHeight(text: string): number {
    const lines = text.split('\n');
    return lines.length * this.lineHeight;
  }

  private drawCenteredText(text: string) {
    const lines = text.split('\n');
    const totalHeight = this.calculateTextHeight(text);
    let lineY = (this.canvasHeight - totalHeight) / 2 + this.fontSize;

    lines.forEach(line => {
      if (line) {
        const textWidth = this.context.measureText(line).width;
        const x = (this.canvasWidth - textWidth) / 2;
        this.context.fillText(line, x, lineY);
        this.context.font = `${this.fontSize}px ${this.typer.fontName}`;
       // this.context.font= `${this.fontSize}vp maozedong`
      }
      lineY += this.lineHeight;
    });
  }
  aboutToAppear() {
    // familyName和familySrc都支持string
    font.registerFont({
      familyName: 'maozedong',
      familySrc: 'font/maozedong.ttf' // font文件与pages目录同级
    })
  }
  build() {
    Column({space: 10}) {
      // 显示字体文件信息,帮助调试


      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        Canvas(this.context)
          .width(this.canvasWidth)
          .height(this.canvasHeight)
          .backgroundColor(Color.White)
          .onReady(() => this.draw())

        Row() {
          Button("开始写字")
            .fontFamily('maozedong')
            .fontSize('16fp')
            .margin({ right: 12 })
            .onClick(() => {
              this.typer.startTyping(() => {
                this.needRedraw = !this.needRedraw;
                this.draw();
              });
            })
            .width('45%')
            .height(40)

          Button("重置")
            .fontSize('16fp')
            .fontFamily('maozedong')
            .margin({ left: 12 })
            .onClick(() => {
              this.typer.resetTyping();
              this.needRedraw = !this.needRedraw;
              this.draw();
            })
            .width('45%')
            .height(40)
        }
        .margin(12)
      }
      .width('99%')
      .height('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.White)
    .padding(5)
  }
}

  

 

卡片:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-container-formlink-V5

ohpm install @mcui/mccharts 
https://gitee.com/openharmony-sig/ohos_axios

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/canvas-drawing
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/custom-font-arkts 自定义字体的注册和使用
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkui-js-full-comp 兼容JS的类Web开发范式(ArkUI.Full)
https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-21 如何加载和使用自定义字体 方舟UI框架(ArkUI)

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ui-js-dev
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/1_3arkts_u8bed_u8a00_u57fa_u7840_u7c7b_u5e93-0000001632530086-V2 ArkTS语言基础类库
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/system-font-arkts
系统字体的信息获取和使用(ArkTS)

posted @ 2025-08-09 19:05  ®Geovin Du Dream Park™  阅读(17)  评论(0)    收藏  举报