TrueType 字体文件是由 一系列 连接 表组成的二进制文件 。每个表都是 a sequence of words ,并有一个称为 Tag 的名称。每个 Tag 都是 uint32 数据类型。文件中的第一个表是 font directory表 ,可以访问字体文件中的其他表。字体数据包含在字体目录表之后的其他表中。由于每个表都可以通过其标签访问,因此这些表可以在文件中以任何顺序出现。

下面是 font directory表

每个项引用到另一张数据表:

head :

此表提供了有关字体的全局信息。应仅使用具有轮廓的轮廓来计算边界框值。在进行这些计算时,应忽略没有轮廓的轮廓。

hhea

此表包含有关水平布局的信息。minRightSidebearing、minLeftSideBearing和xMaxExtent中的值应仅使用具有轮廓的图示符进行计算。在进行这些计算时,应忽略没有轮廓的轮廓。所有保留区域必须设置为0。

maxp

此表确定了此字体的内存要求。带有CFF或CFF2轮廓的字体必须使用此表的0.5版,仅指定numGlyphs字段。带有TrueType轮廓的字体必须使用本表1.0版,其中需要所有数据。

name

命名表允许多语言字符串与 TT™ 字体相关联。这些字符串可以表示版权声明、字体名称、姓氏、样式名称等。为了保持这个表格简短,字体制造商可能希望用一些小语言集制作一组有限的条目。

post

此表包含使用TrueType或OpenType所需的其他信息™ PostScript打印机上的字体。这包括FontInfo字典条目的数据和所有图示符的PostScript名称。有关PostScript名称的更多信息,请参阅Adobe Glyph列表规范。

参考链接:

TrueTypeFont(3)--数据表说明_zhouschina的博客-CSDN博客

GitHub - googlefonts/sfntly: A Library for Using, Editing, and Creating SFNT-based Fonts

Instructing Fonts - TrueType Reference Manual - Apple Developer

TrueType overview - Typography | Microsoft Docs

ttf 文件字体提取 - 简书

ttf文件结构解析 - 一沙 - 博客园

TTF、OTF字体文件结构介绍-马育民老师

  • head 字体头 字体的全局信息
  • cmap 字符代码到图元的映射 把字符代码映射为图元索引
  • glyf 图元数据 图元轮廓定义以及网格调整指令
  • maxp 最大需求表 字体中所需内存分配情况的汇总数据
  • mmtx 水平规格 图元水平规格
  • loca 位置表索引 把元索引转换为图元的位置
  • name 命名表 版权说明、字体名、字体族名、风格名等等
  • hmtx 水平布局 字体水平布局星系:上高、下高、行间距、最大前进宽度、最小左支撑、最小右支撑
  • kerm 字距调整表 字距调整对的数组
  • post PostScript信息 所有图元的PostScript FontInfo目录项和PostScript名
  • PCLT PCL 5数据 HP PCL 5Printer Language 的字体信息:字体数、宽度、x高度、风格、记号集等等
  • OS/2 OS/2和Windows特有的规格 TrueType字体所需的规格集

代码实战:

public void subsetFontFile(File fontFile, File outputFile, int nIters) throws IOException {
    FontFactory fontFactory = FontFactory.getInstance();
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(fontFile);
        byte[] fontBytes = new byte[(int)fontFile.length()];
        fis.read(fontBytes);
        Font[] fontArray = null;
        fontArray = fontFactory.loadFonts(fontBytes);
        Font font = fontArray[0];
        List<CMapTable.CMapId> cmapIds = new ArrayList<CMapTable.CMapId>();
        cmapIds.add(CMapTable.CMapId.WINDOWS_BMP);
        byte[] newFontData = null;
        Font newFont = font;
        if (subsetString != null) {
            Subsetter subsetter = new RenumberingSubsetter(newFont, fontFactory);
            subsetter.setCMaps(cmapIds, 1);
            List<Integer> glyphs = GlyphCoverage.getGlyphCoverage(font, subsetString);
            subsetter.setGlyphs(glyphs);
            Set<Integer> removeTables = new HashSet<Integer>();
            // Most of the following are valid tables, but we don't renumber them yet, so strip
            removeTables.add(Tag.GDEF);
            removeTables.add(Tag.GPOS);
            removeTables.add(Tag.GSUB);
            removeTables.add(Tag.kern);
            removeTables.add(Tag.hdmx);
            removeTables.add(Tag.vmtx);
            removeTables.add(Tag.VDMX);
            removeTables.add(Tag.LTSH);
            removeTables.add(Tag.DSIG);
            removeTables.add(Tag.vhea);
            // AAT tables, not yet defined in sfntly Tag class
            removeTables.add(Tag.intValue(new byte[]{'m', 'o', 'r', 't'}));
            removeTables.add(Tag.intValue(new byte[]{'m', 'o', 'r', 'x'}));
            subsetter.setRemoveTables(removeTables);
            newFont = subsetter.subset().build();
        if (strip) {
            Subsetter hintStripper = new HintStripper(newFont, fontFactory);
            Set<Integer> removeTables = new HashSet<Integer>();
            removeTables.add(Tag.fpgm);
            removeTables.add(Tag.prep);
            removeTables.add(Tag.cvt);
            removeTables.add(Tag.hdmx);
            removeTables.add(Tag.VDMX);
            removeTables.add(Tag.LTSH);
            removeTables.add(Tag.DSIG);
            removeTables.add(Tag.vhea);
            hintStripper.setRemoveTables(removeTables);
            newFont = hintStripper.subset().build();
        FileOutputStream fos = new FileOutputStream(outputFile);
        if (woff) {
            WritableFontData woffData = new WoffWriter().convert(newFont);
            woffData.copyTo(fos);
        } else if (eot) {
            WritableFontData eotData = new EOTWriter(mtx).convert(newFont);
            eotData.copyTo(fos);
        } else {
            fontFactory.serializeFont(newFont, fos);
    } finally {
        if (fis != null) {
            fis.close();
public static List<Integer> getGlyphCoverage(Font font, String string) {
    CMapTable cmapTable = font.getTable(Tag.cmap);
    CMap cmap = getBestCMap(cmapTable);
    Set<Integer> coverage = new HashSet<Integer>();
    coverage.add(0);  // Always include notdef
    // TODO: doesn't support non-BMP scripts, should use StringCharacterIterator instead
    for (int i = 0; i < string.length(); i++) {
      int c = (string.charAt(i)) & 0xffff;
      int glyphId = cmap.glyphId(c);
      touchGlyph(font, coverage, glyphId);
    List<Integer> sortedCoverage = new ArrayList<Integer>(coverage);
    Collections.sort(sortedCoverage);
    return sortedCoverage;
  private static void touchGlyph(Font font, Set<Integer> coverage, int glyphId) {
    if (!coverage.contains(glyphId)) {
      coverage.add(glyphId);
      Glyph glyph = getGlyph(font, glyphId);
      if (glyph != null && glyph.glyphType() == GlyphType.Composite) {
        CompositeGlyph composite = (CompositeGlyph) glyph;
        for (int i = 0; i < composite.numGlyphs(); i++) {
          touchGlyph(font, coverage, composite.glyphIndex(i));
  private static CMap getBestCMap(CMapTable cmapTable) {
    for (CMap cmap : cmapTable) {
      if (cmap.format() == CMapFormat.Format12.value()) {
        return cmap;
    for (CMap cmap : cmapTable) {
      if (cmap.format() == CMapFormat.Format4.value()) {
        return cmap;
    return null;
  private static Glyph getGlyph(Font font, int glyphId) {
    LocaTable locaTable = font.getTable(Tag.loca);
    GlyphTable glyfTable = font.getTable(Tag.glyf);
    int offset = locaTable.glyphOffset(glyphId);
    int length = locaTable.glyphLength(glyphId);
    return glyfTable.glyph(offset, length);
                                    格式表源字体格式FontLab VI提供两种保存的原生文件格式:VFC(二进制)和VFJ(基于JSON)。两者都可以保留字体设计过程中使用的所有元素。以任何其他格式保存实际上是一个导出,可能会丢失一些元素,如字形注释,引脚,元素引用等.VFC更快,但VFJ是人类可读的。FontLab VI格式(VFC)VFC文件格式仅用于FontLab VI(可能还有未来的FontLab应用程序)。它是FontL...
                                    TrueType字体通常包含在单个TrueType字体文件中,其文件后缀为.TTF。OpenType字体是以类似于TrueType字体的格式编码的POSTSCRIPT字体。OPENTYPE字体使用.OTF文件后缀。OPENTYPE还允许把多个OPENTYPE字体组合在一个文件中以利于数据共享。这些字体被称为TrueType字体集(TrueType collection),其文件后缀为.TTC。......
                                    Excel 2013实用技巧教程系列第-6.2-节 单元格格式(字体)职场行走,难免要和对号、方框这些特殊符号打交道,那么如何在Excel中完美输入这些特殊符号呢,今天小编excel小课堂(ID:excel-xiaoketang 长按复制)就跟大家分享下借助特殊字体Winddings输入特殊符号的方法,及如何修改Excel 的默认字体宋体。01输入“R”显示特殊符号“方框对号”  像图...
harfbuzz字体整形引擎的简单介绍
freetype字体引擎的基本原理
truetype&harfbuzz&freetype配合使用,可以渲染出比较特殊的语言,如:高棉语
计算机字体信息技术的发展
在上世纪80 年代,Adobe的Font技术是最...
                                    TrueType字体通常包含在单个TrueType字体文件中,其文件后缀为.TTF。OpenType字体是以类似   于TrueType字体的格式编码的POSTSCRIPT字体。OPENTYPE字体使用.OTF文件后缀。OPENTYPE还允许把多个OPENTYPE字体组合在一个文件中以利于数据共享。这些字体被称为TrueType字体集(TrueType   collection),其文件后缀为.T
                                    TrueType字体
使用SDL渲染文本的一种方法是使用扩展库SDL_ttf。SDL_ttf允许你从TrueType字体中创建图像,我们将在这里使用它从字体文本中创建纹理。
// 使用SDL、SDL_image、SDL_ttf、标准IO、math和string
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <stdio.h>
#include <string&g
                                    打开C:\WINDOWS\Fonts,若里面有MT Extra(TrueType)字体或其快捷方式,则将其删除,再把MathType安装目录下\MathType 6.0\Fonts\TrueType\目录里面的MTEXTRA.TTF字体文件复制粘贴到C:\WINDOWS\Fonts文件夹中(粘贴时会有安装字体提示),完成字体完装。
Glyf 表中包含了字体文件中的定义字形外观的数据。这包括制定描述构成轮廓的轮廓点和网格匹配该字形的指令。
该表支持简单字形和复合字形。字体文件的字形个数由 Head 表中的属性 numberofglyphs 指定,且字形在字体文
件的位置是任意的。
Glyf 表中存在一个 glyphHeaders 表:
int16 numberOfCont...
                                    Windows下字体文件都放在C:\WINDOWS\Fonts目录下,Word里可以通过Ctrl+D修改字体,尤其是插入特殊符号特别有用,例如Windings的N代表海盗旗帜。有图像字体(Windows标准字体,也成为RasterFont),有图形字体(矢量字体),在Nehe13-15课就用到了不同的字体。
1.光栅字体(.FON)
  光栅字体(Raster Font)是针对...