Skip to content

zhiye1995/flutter_noaa_text

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

flutter_noaa_text

🎨 基于路径绘制的无抗锯齿文本渲染库 - Flutter NoaaText

没有抗锯齿的字体渲染解决方案,通过直接绘制 TTF 字体的矢量路径实现精确控制。

✨ 特性

  • 🎯 路径绘制 - 解析 TTF 字体文件,通过矢量路径精确绘制文本,完全控制渲染过程
  • 🚫 无抗锯齿 - 完全禁用抗锯齿,获得清晰锐利的边缘,适合像素艺术和特殊显示需求
  • 💪 合成加粗 - 使用描边+填充技术模拟加粗效果,无需额外字体文件,支持动态调整加粗程度
  • 📐 合成斜体 - 使用 Matrix4 剪切变换实现斜体效果,倾斜角度可调节(默认约 12 度)
  • 📝 自动换行 - 支持根据容器宽度自动换行,智能处理中英文混排,基于字体的实际 advanceWidth 精确计算
  • 🔄 字体缓存 - 内置 LRU 缓存策略的字体管理器,自动缓存已加载字体,避免内存溢出
  • 🎨 备用字体 - 支持备用字体列表,当主字体缺少某些字符时自动降级使用备用字体
  • 路径缓存 - 绘制器内置路径缓存机制,提高重绘性能,减少路径生成开销
  • 🚀 Isolate 优化 - 批量加载字体时支持在独立线程解析,避免 UI 卡顿
  • 📏 精确度量 - 基于 TTF 字体的 unitsPerEmadvanceWidth 精确计算字符宽度和位置

📦 依赖

本项目依赖独立的 TTF 字体解析库:

dependencies:
  flutter_ttf_parser:
    git:
      url: https://gitea.cquni.com/yangjie/flutter_ttf_parser.git

🚀 快速开始

1. 添加依赖

# pubspec.yaml
dependencies:
  flutter_noaa_text:
    git:
      url: [你的仓库地址]

2. 添加字体文件

# pubspec.yaml
flutter:
  assets:
    - assets/fonts/song_ti.ttf
    - assets/fonts/source_han_sans.ttf  # 备用字体(可选)

3. 基础使用

import 'package:flutter_noaa_text/flutter_noaa_text.dart';

// 加载字体
final fontManager = NoaaFontManager();
final font = await fontManager.loadFont('assets/fonts/song_ti.ttf');

// 基础文本显示
NoaaText(
  '你好世界 Hello World',
  font: font,
  style: NoaaTextStyle(
    fontSize: 48.0,
    color: Colors.black,
  ),
)

4. 高级功能

4.1 合成加粗和斜体

NoaaText(
  '加粗斜体文本',
  font: font,
  style: NoaaTextStyle(
    fontSize: 48.0,
    color: Colors.black,
    fontWeight: FontWeight.bold,      // 合成加粗
    fontStyle: FontStyle.italic,      // 合成斜体
    boldStrokeWidthFactor: 0.03,      // 加粗描边宽度系数(可选)
    italicSkewFactor: -0.2,           // 斜体剪切系数(可选)
    disableAntiAlias: true,           // 禁用抗锯齿
  ),
)

4.2 使用备用字体

当主字体缺少某些字符(如生僻字)时,自动使用备用字体:

// 加载主字体和备用字体
final mainFont = await fontManager.loadFont('assets/fonts/song_ti.ttf');
final fallbackFont = await fontManager.loadFont('assets/fonts/source_han_sans.ttf');

NoaaText(
  '常用字和生僻字:焜燊',
  font: mainFont,
  style: NoaaTextStyle(
    fontSize: 48.0,
    fontFamilyFallback: [fallbackFont],  // 备用字体列表
  ),
)

4.3 自动换行和行高

NoaaText(
  '这是一段很长的文本,会根据容器宽度自动换行。支持中英文混排,English and Chinese mixed layout.',
  font: font,
  style: NoaaTextStyle(
    fontSize: 24.0,
    height: 1.5,              // 行高倍数(1.5 倍行距)
    letterSpacing: 2.0,       // 字符间距(像素)
  ),
  maxLines: 3,                // 最大行数
  softWrap: true,             // 启用自动换行
)

4.4 字体批量预加载

提升应用启动后的渲染性能:

final fontManager = NoaaFontManager();

// 方式1: 主线程预加载(适合少量字体)
await fontManager.preloadFonts([
  'assets/fonts/song_ti.ttf',
  'assets/fonts/hei_ti.ttf',
]);

// 方式2: Isolate 预加载(适合大量字体,避免 UI 卡顿)
await fontManager.preloadFontsWithIsolate(
  [
    'assets/fonts/song_ti.ttf',
    'assets/fonts/hei_ti.ttf',
    'assets/fonts/kai_ti.ttf',
    // ... 更多字体
  ],
  onProgress: (loaded, total, path) {
    print('加载进度: $loaded/$total - ${path ?? "完成"}');
  },
);

📖 核心组件详解

NoaaText Widget

无抗锯齿文本渲染组件,支持自动换行、多行显示等。

主要参数:

参数 类型 说明
text String 要显示的文本内容
font TtfFont TTF 字体对象(必需)
style NoaaTextStyle? 文本样式配置
softWrap bool? 是否自动换行(默认 true
maxLines int? 最大行数(null 表示无限制)
textAlign TextAlign? 文本对齐方式(暂未实现)
overflow TextOverflow? 溢出处理(暂未实现)

NoaaTextStyle

文本样式配置类,参考 Flutter TextStyle API 设计。

主要属性:

属性 类型 默认值 说明
fontSize double? 14.0 字体大小(像素)
color Color? Color(0xFF000000) 文本颜色
fontWeight FontWeight? FontWeight.normal 字体粗细(bold 触发合成加粗)
fontStyle FontStyle? FontStyle.normal 字体样式(italic 触发合成斜体)
letterSpacing double? 0.0 字符间距(像素)
height double? 1.0 行高倍数
disableAntiAlias bool? true 是否禁用抗锯齿
boldStrokeWidthFactor double? 0.03 加粗描边宽度系数(相对字号)
italicSkewFactor double? -0.2 斜体剪切系数(约 12 度倾斜)
fontFamilyFallback List<TtfFont>? null 备用字体列表

NoaaFontManager

字体缓存管理器,采用单例模式,提供字体加载和缓存功能。

核心特性:

  • LRU 缓存策略 - 最大缓存 20 个字体,自动淘汰最久未使用的字体
  • 并发控制 - 防止同一字体被重复加载
  • 内存优化 - 每个字体对象只创建一次,全局复用
  • Isolate 支持 - 批量加载时在独立线程解析,避免 UI 卡顿

常用方法:

final fontManager = NoaaFontManager();

// 加载单个字体(如已缓存则直接返回)
final font = await fontManager.loadFont('assets/fonts/song_ti.ttf');

// 检查字体是否已缓存
bool isCached = fontManager.isCached('assets/fonts/song_ti.ttf');

// 获取已缓存的字体(不触发加载)
TtfFont? cachedFont = fontManager.getCachedFont('assets/fonts/song_ti.ttf');

// 清除缓存
fontManager.clearCache();  // 清除所有
fontManager.clearCache('assets/fonts/song_ti.ttf');  // 清除指定字体

// 查看缓存统计
Map<String, dynamic> stats = fontManager.getCacheStats();
print('当前缓存数量: ${stats['cacheSize']}/${stats['maxCacheSize']}');

NoaaTextPainter

自定义文本绘制器,基于 CustomPainter 实现。

核心机制:

  • 路径缓存 - 缓存每行文本的矢量路径,避免重复生成
  • 🎯 精确定位 - 基于字体的 ascenderadvanceWidth 精确定位字符
  • 🎨 变换缓存 - 缓存斜体变换后的路径,提高重绘性能
  • 🔄 智能重绘 - 通过 shouldRepaint 判断是否需要重绘,优化性能

NoaaTextUtils

工具类,提供路径生成、变换和绘制等核心功能。

主要方法:

// 生成文本路径
Path path = NoaaTextUtils.generatePathForText(text, font, style);

// 应用斜体变换
Path italicPath = NoaaTextUtils.applyItalicTransform(path, skewFactor);

// 计算文本宽度
double width = NoaaTextUtils.calculateTextWidth(text, font, fontSize, letterSpacing);

// 文本自动换行
List<String> lines = NoaaTextUtils.wrapText(text, font, maxWidth, fontSize, letterSpacing);

// 绘制加粗路径
NoaaTextUtils.drawBoldPath(canvas, path, color, fontSize, strokeWidthFactor, disableAntiAlias);

// 绘制普通路径
NoaaTextUtils.drawNormalPath(canvas, path, color, disableAntiAlias);

🔧 技术实现

字体解析

使用 flutter_ttf_parser 解析 TTF 字体文件:

  1. 读取字体表 - 解析 cmap(字符映射)、glyf(字形轮廓)、hmtx(水平度量)等表
  2. 字符到字形 - 通过 cmap 表将 Unicode 字符映射到 Glyph ID
  3. 提取轮廓 - 从 glyf 表读取字形的贝塞尔曲线控制点
  4. 生成路径 - 将控制点转换为 Flutter Path 对象

路径绘制

基于 Canvas 绘制矢量路径:

// 1. 计算缩放比例(字体单位 → 像素单位)
final scale = fontSize / font.unitsPerEm;

// 2. 生成字符路径
final path = font.generatePathForCharacter(charCode);

// 3. 应用变换(缩放、平移)
final transformedPath = TtfTransform.moveAndScale(path, x, y, scale, scale);

// 4. 绘制到画布
canvas.drawPath(transformedPath, paint);

合成加粗

使用 描边 + 填充 双重绘制技术:

// 1. 先绘制描边(增加笔画宽度)
paint.style = PaintingStyle.stroke;
paint.strokeWidth = fontSize * 0.03;  // 约 3% 字号宽度
canvas.drawPath(path, paint);

// 2. 再填充中心
paint.style = PaintingStyle.fill;
canvas.drawPath(path, paint);

效果: 两次绘制叠加产生加粗效果,无需 Bold 字体文件。

合成斜体

使用 Matrix4 水平剪切变换

// 剪切矩阵(Skew Transform)
final matrix = Matrix4.identity()
  ..setEntry(0, 1, -0.2);  // 水平剪切,向右倾斜约 12 度

// 应用变换
final italicPath = path.transform(matrix.storage);

变换公式:

x' = x + y * skewFactor
y' = y

其中 skewFactor = -0.2 约等于 tan(12°)

自动换行

基于 TTF 水平度量 精确计算字符宽度:

// 1. 获取字形的 advanceWidth(字体单位)
final glyphId = font.getGlyphIdForCharacter(charCode);
final advanceWidth = font.getAdvanceWidthForGlyphId(glyphId);

// 2. 转换为像素单位
final charWidth = advanceWidth * (fontSize / font.unitsPerEm) + letterSpacing;

// 3. 累加宽度,超出则换行
if (currentWidth + charWidth > maxWidth && currentLine.isNotEmpty) {
  lines.add(currentLine);
  currentLine = char;
  currentWidth = charWidth;
} else {
  currentLine += char;
  currentWidth += charWidth;
}

备用字体降级

当主字体缺少字符时自动降级:

// 1. 尝试主字体
Path charPath = font.generatePathForCharacter(charCode);

// 2. 检查路径是否为空
if (charPath.getBounds().isEmpty && fallbackFonts.isNotEmpty) {
  // 3. 遍历备用字体
  for (final fallbackFont in fallbackFonts) {
    final fallbackPath = fallbackFont.generatePathForCharacter(charCode);
    if (!fallbackPath.getBounds().isEmpty) {
      charPath = fallbackPath;
      effectiveFont = fallbackFont;
      break;
    }
  }
}

📱 示例应用

运行示例应用查看完整功能:

cd example
flutter run

示例功能

  • 🎨 多种字体选择 - 宋体、楷体、黑体、方正魏碑等 11 种字体
  • 📏 字体大小调整 - 12px ~ 120px 动态调节
  • 💪 加粗/斜体切换 - 实时切换合成加粗和斜体效果
  • 📊 对比视图 - 与 Flutter 原生 Text 对比显示
  • 📸 导出图片 - 支持导出为 PNG 图片(Android 需权限)
  • ℹ️ 字体信息 - 查看字体详细信息(unitsPerEm、ascender、descender 等)
  • 🔍 字体比对页面 - 批量加载字体进行对比(使用 Isolate 优化)

示例代码结构

example/
├── lib/
│   ├── main.dart                    # 主页面:基础功能演示
│   ├── font_comparison_page.dart    # 字体比对页面
│   └── font_config.dart             # 字体配置(中文名 ↔ 文件名映射)
├── assets/
│   └── fonts/                       # 字体文件
│       ├── song_ti.ttf
│       ├── kai_ti.ttf
│       └── ...
└── pubspec.yaml

🛠️ 开发

获取依赖

flutter pub get

分析代码

flutter analyze

运行测试

flutter test

🎯 使用场景

适用场景

像素艺术风格应用 - 需要硬边缘、无抗锯齿的像素风格文本
精确渲染控制 - 需要对文本渲染的每个细节进行精确控制
跨平台一致性 - 需要在不同平台保持完全一致的渲染效果
特殊显示需求 - LED 屏幕、单色显示器等特殊硬件
路径动画 - 需要对字形路径进行特殊处理(如动画、变形)
字体缺失场景 - 使用备用字体处理生僻字或特殊字符

不适用场景

常规文本显示 - 推荐使用 Flutter 原生 Text Widget(性能更优)
富文本编辑 - 不支持富文本、链接、内联图片等复杂功能
极大量文本 - 长文章、书籍等大量文本渲染性能较低

📚 最佳实践

1. 字体加载优化

// ✅ 推荐:应用启动时预加载常用字体
class MyApp extends StatefulWidget {
  @override
  void initState() {
    super.initState();
    _preloadFonts();
  }

  Future<void> _preloadFonts() async {
    final fontManager = NoaaFontManager();
    await fontManager.preloadFontsWithIsolate([
      'assets/fonts/song_ti.ttf',
      'assets/fonts/source_han_sans.ttf',
    ]);
  }
}

// ❌ 不推荐:每次使用时都加载
Widget build(BuildContext context) {
  return FutureBuilder(
    future: NoaaFontManager().loadFont('assets/fonts/song_ti.ttf'),
    builder: (context, snapshot) { ... },
  );
}

2. 备用字体配置

// ✅ 推荐:使用覆盖范围广的字体作为备用
final mainFont = await fontManager.loadFont('assets/fonts/song_ti.ttf');
final fallbackFont = await fontManager.loadFont('assets/fonts/source_han_sans.ttf');  // 思源黑体覆盖更多字符

NoaaText(
  '常用字和生僻字混排',
  font: mainFont,
  style: NoaaTextStyle(
    fontFamilyFallback: [fallbackFont],
  ),
)

3. 性能优化

// ✅ 推荐:复用字体对象
final font = await fontManager.loadFont('assets/fonts/song_ti.ttf');

// 多个地方使用同一个 font 对象
NoaaText('文本1', font: font, ...)
NoaaText('文本2', font: font, ...)
NoaaText('文本3', font: font, ...)

// ❌ 不推荐:重复加载
NoaaText('文本1', font: await fontManager.loadFont(...), ...)
NoaaText('文本2', font: await fontManager.loadFont(...), ...)

4. 样式管理

// ✅ 推荐:定义全局样式常量
class AppTextStyles {
  static const heading = NoaaTextStyle(
    fontSize: 48.0,
    fontWeight: FontWeight.bold,
    height: 1.2,
  );

  static const body = NoaaTextStyle(
    fontSize: 24.0,
    height: 1.5,
  );
}

// 使用
NoaaText('标题', font: font, style: AppTextStyles.heading)

📄 许可证

请查看 LICENSE 文件。

🔗 相关项目

🤝 贡献

欢迎提交 Issue 和 Pull Request!

📋 详细文档

  • 库文档 - 完整的 API 文档和实现说明
  • 迁移说明 - 从本地 ttf_parser 迁移到外部库的说明
  • 示例应用 - 完整的示例应用(如果存在)

⚠️ 注意事项

  1. 性能考虑 - 路径绘制比原生文本渲染更耗性能,不适合大量文本
  2. 字体许可 - 确保你有权使用项目中的字体文件
  3. 发布限制 - 本项目使用 Git 依赖,设置了 publish_to: none,暂不支持发布到 pub.dev
  4. 平台兼容 - 理论上支持所有 Flutter 平台,但主要在 Windows、Android 上测试

Made with ❤️ by Flutter Community

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors