自定义文本绘制与显示(C/C++)
在复杂的文本排版场景中,当系统提供的标准文本组件无法满足特定的视觉或交互需求时,开发者可以利用ArkGraphics 2D提供的底层文本绘制能力,通过直接控制画布(Canvas)和文本样式,实现对文本外观、布局的精细控制。这种能力适用于需要高度定制化文本渲染效果的场景,例如艺术字体、复杂的富文本编排或特殊的动态文字效果。
字体引擎作为图形系统中的核心组件,负责将字符代码转换为可视化的字形,并精确计算每个字形的布局和位置,为自定义文本绘制提供底层支持。通过文本测量接口,开发者可以获取文本的精确尺寸,这是实现精准布局(如居中显示)的基础。
文本塑形
场景介绍
文本塑形是字体引擎提供的一项关键能力,它允许开发者不经过系统默认的文本排版流程,直接获取文本的底层字形信息(如宽度、方向等测量信息)。这使得开发者能够基于这些原始数据,实现完全自定义的排版逻辑、绘制操作以及断行策略。
这种能力适用于以下场景:
- 自定义富文本渲染:例如在社交媒体、新闻客户端等应用中,需要实现图文混排、多样式文本混合显示。
- 跨平台一致性排版需求应用:确保文本在不同平台或设备上呈现一致的视觉效果。
- 精细化排版管理:如实现艺术排版、动态文字布局等系统标准文本组件难以达到的效果。
接口说明
文本塑形中常用接口如下表所示,详细接口说明参考drawing_text_typography.h和drawing_text_blob.h。
| 接口名 | 描述 |
|---|---|
| OH_Drawing_LineTypography* OH_Drawing_CreateLineTypography(OH_Drawing_TypographyCreate* handler) | 创建一个排版行对象OH_Drawing_LineTypography的指针,排版行对象保存着文本内容以及样式的载体,可以用于计算单行排版信息。 |
| OH_Drawing_TextLine* OH_Drawing_LineTypographyCreateLine(OH_Drawing_LineTypography* lineTypography,size_t startIndex, size_t count) | 根据指定区间文本内容创建一个指向文本行对象OH_Drawing_TextLine的指针。 |
| OH_Drawing_Array* OH_Drawing_TextLineGetGlyphRuns(OH_Drawing_TextLine* line) | 获取文本行对象中的文本渲染单元数组。 |
| OH_Drawing_Array* OH_Drawing_GetRunGlyphs(OH_Drawing_Run* run, int64_t start, int64_t length) | 获取渲染单元指定范围内的字形数组。 |
| OH_Drawing_Font* OH_Drawing_GetRunFont(OH_Drawing_Run* run) | 获取渲染单元字体对象。 |
| OH_Drawing_Array* OH_Drawing_GetRunGlyphAdvances(OH_Drawing_Run* run, uint32_t start, uint32_t length) | 获取渲染单元字体宽度数组。 |
| OH_Drawing_TextBlobBuilder* OH_Drawing_TextBlobBuilderCreate(void) | 用于创建一个文本构造器对象。 |
| OH_Drawing_TextBlob* OH_Drawing_TextBlobBuilderMake(OH_Drawing_TextBlobBuilder* textBlobBuilder) | 用于从文本构造器中创建文本对象。 |
| void OH_Drawing_CanvasDrawTextBlob(OH_Drawing_Canvas* canvas, const OH_Drawing_TextBlob* textBlob, float x, float y) | 用于画一段文字。 |
开发步骤
从API version 18开始,支持获取文字塑形结果能力。从API version 20开始,支持获取文字排版方向和文字字形宽度。关键代码如下:
-
在工程的src/main/cpp/CMakeLists.txt文件中添加以下lib。
libnative_drawing.so -
导入依赖的相关头文件。
#include <native_drawing/drawing_font_collection.h>#include <native_drawing/drawing_text_typography.h>#include <native_drawing/drawing_text_blob.h>#include <native_drawing/drawing_text_line.h>#include <native_drawing/drawing_text_run.h>#include <native_drawing/drawing_text_lineTypography.h>#include <native_drawing/drawing_rect.h>#include <native_drawing/drawing_point.h> -
创建段落样式,并使用构造段落生成器ParagraphBuilder生成段落实例。
// 创建一个 TypographyStyle,创建 TypographyCreate 时需要使用OH_Drawing_TypographyStyle *typoStyle = OH_Drawing_CreateTypographyStyle();// 设置文字颜色、大小、字重,不设置 TextStyle 会使用 TypographyStyle 中的默认 TextStyleOH_Drawing_TextStyle *txtStyle = OH_Drawing_CreateTextStyle();OH_Drawing_SetTextStyleFontSize(txtStyle, DIV_TEN(width_));// 创建 FontCollection,FontCollection 用于管理字体匹配逻辑OH_Drawing_FontCollection *fc = OH_Drawing_CreateSharedFontCollection();// 使用 FontCollection 和 之前创建的 TypographyStyle 创建 TypographyCreate。TypographyCreate 用于创建 TypographyOH_Drawing_TypographyCreate *handler = OH_Drawing_CreateTypographyHandler(typoStyle, fc); -
设置文本样式,添加文本内容。
// 设置文本内容,并将文本添加到 handler 中OH_Drawing_TypographyHandlerPushTextStyle(handler, txtStyle);const char *text = "Hello World";OH_Drawing_TypographyHandlerAddText(handler, text); -
创建行对象。获取行中所有文字的塑形结果。
使用OH_Drawing_LineTypographyCreateLine()方法创建一个单行对象,通过行对象OH_Drawing_TextLineGetGlyphRuns()方法获取相同样式的文字单元。
// 通过 handler 创建一个 TypographyOH_Drawing_LineTypography *lineTypography = OH_Drawing_CreateLineTypography(handler);// 创建一个 TextLine,取(0, 11)的字符OH_Drawing_TextLine *textLine = OH_Drawing_LineTypographyCreateLine(lineTypography, 0, 11);// 获取塑形结果OH_Drawing_Array *runs = OH_Drawing_TextLineGetGlyphRuns(textLine); -
该步骤是文本塑形流程中的自定义绘制环节。通过调用OH_Drawing_GetRunGlyphs()方法获取文本中每个字符对应的字形序号,再结合OH_Drawing_GetRunFont()方法获取的字体对象,即可唯一确定每个字形的具体图形信息。
从 API version 20 开始,新增的OH_Drawing_GetRunGlyphAdvances()方法能够返回一个数组,其中包含了每个字形在绘制时建议占用的宽度和高度。依赖这些精确的测量数据,开发者可以自由地计算并定义每个字形的绘制位置,从而实现复杂的文本布局效果,如自定义字符间距、垂直偏移或特殊排版。
size_t runsLength = OH_Drawing_GetDrawingArraySize(runs);for (int i = 0; i < runsLength; i++) {OH_Drawing_Run *run = OH_Drawing_GetRunByIndex(runs, i);// 获取所有字形数据OH_Drawing_Array *glyphs = OH_Drawing_GetRunGlyphs(run, 0, 0);size_t glyphsLength = OH_Drawing_GetDrawingArraySize(glyphs);// 获取相同绘制单元字体OH_Drawing_Font *font = OH_Drawing_GetRunFont(run);OH_Drawing_Array *advances = OH_Drawing_GetRunGlyphAdvances(run, 0, 0);OH_Drawing_TextBlobBuilder *builder = OH_Drawing_TextBlobBuilderCreate();// 创建一个20*20的矩形OH_Drawing_Rect *rect = OH_Drawing_RectCreate(0, 0, 20, 20);const OH_Drawing_RunBuffer *buffer = OH_Drawing_TextBlobBuilderAllocRunPos(builder, font, glyphsLength, rect);// 创建字形buffer,通过drawing接口进行字形独立绘制int x = 0;int y = 0;for (int index = 0; index < glyphsLength; index++) {buffer->glyphs[index] = OH_Drawing_GetRunGlyphsByIndex(glyphs, index);// 设置字形位置buffer->pos[index * TWO_INT] = x;buffer->pos[index * TWO_INT + 1] = y;OH_Drawing_Point *advance = OH_Drawing_GetRunGlyphAdvanceByIndex(advances, index);float pos = 0;OH_Drawing_PointGetX(advance, &pos);x += pos + 10; // 每个字形间水平间隔10pxOH_Drawing_PointGetY(advance, &pos);y += pos + 30; // 每个字形间垂直间隔30px}// 自定义绘制一串具有相同属性的一系列连续字形OH_Drawing_TextBlob *textBlob = OH_Drawing_TextBlobBuilderMake(builder);// 将文本绘制到画布(20,100)上OH_Drawing_CanvasDrawTextBlob(cCanvas_, textBlob, 20, 100);// 释放内存OH_Drawing_TextBlobDestroy(textBlob);OH_Drawing_FontDestroy(font);OH_Drawing_DestroyRunGlyphAdvances(advances);OH_Drawing_DestroyRunGlyphs(glyphs);} -
释放内存
// 释放内存OH_Drawing_DestroyTypographyStyle(typoStyle);OH_Drawing_DestroyTextStyle(txtStyle);OH_Drawing_DestroyFontCollection(fc);OH_Drawing_DestroyTypographyHandler(handler);OH_Drawing_DestroyLineTypography(lineTypography);OH_Drawing_DestroyTextLine(textLine);OH_Drawing_DestroyRuns(runs);
效果展示:
