TrueTypeFont(2)--如何将转换为在 Windows 95 中的 TrueType 标志符号索引的 Unicode 字符代码

应用程序通常绘制 Unicode 文本中的单词的字符串中指定的字符代码。当使用 TrueType 字体时,操作系统将 TrueType 标志符号索引转换为这些字符代码时,它绘制字形。应用程序可能需要转换到标志符号索引的字符代码。这篇文章的示例源代码,讨论如何从 TrueType 字体文件获取标志符号索引。

 

绘制到设备上的字符在 Windows 中涉及到的字符图形 (标志符号) 的字体取决于索引映射的字符代码。当使用 TrueType 字体时,这些索引被称为标志符号索引。

TrueType 字体文件有一种灵活的、 面向表的文件格式。这种文件格式的灵活性支持到各自的标志符号的字符代码各种字符编码 (或映射)。一种编码这种方式可以为 Unicode 到标志符号索引。

Microsoft Windows 操作系统中使用 TrueType 字体文件,几乎始终是 Unicode 编码。即使 Windows 95 (和 Windows 3.1 到其前置任务版本) 使用 TrueType 字体作为其内部编码使用 Unicode 标准。Unicode 标准和 TrueType 字体文件,请参阅详细信息本文的参考部分。

作为两个单独的入口点导出函数的 Win32 应用程序编程接口 (API)。第一个具有基本的函数名称,加上附加"A";第二个具有基本的函数名称,加上附加的"W"。字符组 (或 ANSI) 的字符串编码这些入口点支持和 Unicode (或宽字符) 编码,分别。平台 SDK 头文件定义基本的函数名称为"A"或"W"的款式,具体取决于使用 UNICODE 的宏,但可以直接引用任何一个 variant 类型的值。

大多数 Win32 API 中的 Windows 95 或更高版本中的功能支持函数 ANSI 版本。除了 ANSI API 中,它还支持有限子集的 Unicode 支持的功能,如TextOutWlstrlenW,等等。在 Windows 95 和其后续任务的 Unicode 支持,请参阅详细信息的参考部分。

这一组 Unicode 或宽字符函数支持编写有限的 Unicode 支持的应用程序。正如 ANSI 应用程序可能需要使用标志符号索引,Unicode 支持的应用程序可能需要对它们的访问也。当应用程序使用 TrueType 字体文件,一些高级的功能,如果应用程序需要的标志符号定义,或者是否需要实施的解决办法或否则为有操作系统中的功能,就会发生这种情况。

ANSI 应用程序可以使用GetCharacterPlacementA函数来转换的字符代码,标志符号索引的一个字节的字符串。如果应用程序使用 Unicode 字符串编码,并且它在 Windows NT 上运行,则GetCharacterPlacement函数的宽字符版本。在 Windows 95 中,因此,没有将 Unicode 转换为标志符号索引的 API 未实现GetCharacterPlacementW函数。

将 Unicode 字符代码转换为 TrueType 标志符号索引中,必须在应用程序中检索表数据中的标志符号索引 Unicode 编码。TrueType 字体文件中的数据可以通过调用GetFontData Win32 函数来获取。此函数可返回一个未处理的字节的缓冲区,但它可以相对于指定的 TrueType 表此缓冲区中提取。函数还 TrueType 字体文件当前已实现设备上下文 (DC) 上的操作。这些功能使函数比定位字体文件,并分析其表目录定位到相应的表的另一种更有用。

标志符号索引 Unicode 编码位于目录中的 TrueType 字体文件标记为"cmap",它是包含字体文件的字符映射表的标记名称。此表可能包含一个或多个选的子表的不同的映射。

紧随"cmap"表中的初始的无符号短值为 TrueType 字体文件包含每个编码的目录。TrueType 规范,Unicode 编码都位于标有 PlatformId 值为 3 和 SpecificId 的值为 1 的子表。规范还定义了引用的格式 4 子表此 3-1 编码的子表。用来为 3 PlatformId 和 SpecificId 0 (零) 的编码格式 4 也是编码,但字体文件通常被解释为符号字体文件。每个符号字体中的 TrueType 规范建议,人们预计该编码包含字符代码从 Unicode 专用区。

格式 4 子表是稀疏数组。为了适应所需的 16 位 Unicode 标准的 64k 项,格式 4 子表分成收集相邻的字符序列。段由受该范围的字符的第一个和最后一个字符代码值定义。段的集合由一套并行数组存储在子表中: 一个范围 (startCount) 和一个用于结束字符范围 (endCount) 的起始字符。段数组是一个字符代码的潜在映射,不实际映射分类。

标志符号索引映射需要从子表名为 idRangeOffset 的第三个并行数组的使用。这决定了这两种方法用于计算最终的标志符号索引。第一种方法使用一个简单的增量值来计算字符代码中的标志符号索引。第二种方法使用到中间的标志符号 Id 表查找。如果此表中的值为零,没有任何标志符号 ;否则,将使用的值到最终的标志符号索引的计算机。查阅表格是表示集合的跨越一段的不连续的字符代码的一个有效方法。

余额的这篇文章介绍了如何从 TrueType 字体文件获取 TrueType 标志符号索引。进行了以下假设: 转换应用程序的域控制器当前所选的 true Type 字体、 当前字符串编码为 Unicode,和 TrueType 字体文件具有 Unicode 字符映射表 ("cmap"格式 4 子表)。

注意: 这种技术用于获取标志符号索引是适用于 Windows NT ;但是, GetCharacterPlacement函数的 Unicode 版本已实现,因为它通常是不必要。如果 Windows 95 应用程序的字符串不是 Unicode,它可以调用GetCharacterPlacementA函数,而是将转换为标志符号索引。

完整的源代码是这篇文章的末尾。在文章的文本,进行大量引用源代码。请参阅示例源代码,以检查其完整的上下文中相关的参照。

几率和结束

若要使用 TrueType 字体文件中的数据,许多数据类型问题必须很好地理解。TrueType 规范,在字体文件中的所有表都定义为基本数据类型的集合。规范还定义的基础数据类型,但它们很好地对应的一些基本数据类型定义的 Windows 平台 SDK。

TrueType 字体文件是字节压缩的这意味着所有数据类型都位于在文件中的字节偏移量。特定的填充字节包含在表定义中 (如果适用) 的规格。大多数编译器允许,有时会默认为字节边界以外结构上的对齐方式。这意味着定义"C"语言结构,以模仿表的定义可能不兼容。

此示例代码中使用结构定义,如有可能,来表示表。要正常工作,必须编译代码并且字节结构对齐方式如确保通过 pack 杂注的示例代码使用。

保证字节装箱,即使只在一个结构中读取表不正确。TrueType 字体文件使用"大字节序"或摩托罗拉样式字节排序,而英特尔的微处理器使用"小字节序"字节排序。这意味着所有大于从 TrueType 字体文件中获取一个字节必须有个字节的数据交换。换用的字节会使数据兼容与英特尔的微处理器。SWAPWORD 和 SWAPLONG 的宏定义提供一个工具,以便执行此操作。

定义

"Cmap"表中的 TrueType 字体文件包含多个子表 ;其中的每个定义不同的编码。要找到各个子表,该代码示例还定义了方便宏调用 CMAPHEADERSIZE。宏是子表目录的开头的偏移量计算方便。该宏将返回两个无符号短数据类型用于存储"cmap"表中的"cmap"表版本和各个子表数的大小:

/*  CMAP table Data
    From the TrueType Spec revision 1.66

    USHORT  Table Version #
    USHORT  Number of encoding tables
*/ 
#define     CMAPHEADERSIZE  (sizeof(USHORT)*2)

每个编码的子表主"cmap"表中有一个目录条目。它是通过结构定义 _CMapEncoding 表示在源代码中。此结构包含两个 ID 字段,用来区分各个子表和从"cmap"表中的子表所在的位置开始的偏移量:

typedef struct _CMapEncoding
{
    USHORT  PlatformId;
    USHORT  EncodingId;
    ULONG   Offset;
} CMAPENCODING;

Win32 API 中的GetFontData函数采用一个 dword 值参数,作为表名。这样做的原因 TrueType 规范定义表名称为四字节标记序列。正确打包到GetFontData函数调用的 DWORD 参数的表名称,该代码示例定义一个名为 MAKETABLENAME。宏的工作方式通过按顺序将四个表名称标记的单个字节值转移到 dword 值数据类型:

// Macro to pack a TrueType table name into a DWORD.
#define     MAKETABLENAME(ch1, ch2, ch3, ch4) (\ 
    (((DWORD)(ch4)) << 24) | \ 
    (((DWORD)(ch3)) << 16) | \ 
    (((DWORD)(ch2)) << 8) | \ 
    ((DWORD)(ch1)) \ 
    )	

Unicode 编码的子表标记为 3 PlatformId 和 SpecificId 的 1。这"3-1"编码是根据 TrueType 规范格式 4 子表。在源代码中定义是一种结构,_CMap4,它对应于格式 4 子表的前七位的数据类型加上一个符号数组的一个符号的短整型。数组表示子表定义多个无符号短阵列组成的平衡。通过结构定义中包含数组符号,定义为无符号的短数组的开始了方便地址。数组符号然后可以用于使用它们从第一个数组的偏移量计算其他数组的起始地址。当强制转换包含完整的子表,来取消引用某个无符号短数组更大的内存缓冲区时,这非常有用:

typedef struct _CMap4   // From the TrueType Spec. revision 1.66.
{
    USHORT format;          // Format number is set to 4. 
    USHORT length;          // Length in bytes. 
    USHORT version;         // Version number (starts at 0).
    USHORT segCountX2;      // 2 x segCount
    USHORT searchRange;     // 2 x (2**floor(log2(segCount)))
    USHORT entrySelector;   // log2(searchRange/2)
    USHORT rangeShift;      // 2 x segCount - searchRange

    USHORT Arrays[1];       // Placeholder symbol for address of arrays. following.
} CMAP4, *LPCMAP4;
		
进程

该代码示例实现了这两个基本任务: Unicode"cmap"子字体文件和子表,以查找 Unicode 字符码的 TrueType 标志符号索引的搜索,从表中检索。

若要从给出一个 DC 的 TrueType 字体文件中检索表数据,该代码使用GetFontData函数。此函数需要到 DC TrueType 字体文件处于选中状态。若要检索由指定的 TrueType 表索引的表数据,四字节表标记名称必须打包到一个 dword 值中。因为我们只是想从"cmap"表中获取数据,代码示例定义了一个全局的 dword 值,dwCmapName,它汇集了"cmap"标记。GetFontData函数的所有调用经过都编码,可以使用 dwCmapName 的全局变量。

GetTTUnicodeCoverage函数是 Unicode"cmap"子表中检索的源代码。它被声明为:

BOOL GetTTUnicodeCoverage ( 
    HDC hdc,            // DC with TT font.
    LPCMAP4 pBuffer,    // Properly allocated buffer.
    DWORD cbSize,       // Size of properly allocated buffer.
    DWORD *pcbNeeded    // Size of buffer needed.
    )

此函数从 TrueType"cmap"表中检索完整 Unicode 子表。如果使用的缓冲区太小,调用 (即,大小为零) pBuffer 参数声明为 cbSize 或无效,因为它运行失败并返回 FALSE。当以这种方式失败时,它计算,并返回在 pcbNeeded 参数中所需的缓冲区大小。如果此函数成功,填充 pBuffer 参数并放在 pcbNeeded 参数中复制的字节数。

此功能将首先搜索子表将包含 Unicode 编码,或者"编码 3-1"或"3-0"编码。TrueType 规范的"cmap"数据类型章中定义,这些都是格式 4 各个子表。

如果函数会查找 Unicode 编码,它将使用GetFontFormat4Header函数检索格式 4 子表中的前七个元素。该代码然后计算返回整个 Unicode 子表所需的缓冲区大小。如果缓冲区太小或不提供,大小返回给调用方,这样他们可以分配一个适当大小的缓冲区并重新调用函数。

如果呼叫者提供的缓冲区足够大,示例代码然后使用GetFontFormat4Subtable函数来检索整个子表。此函数将适当地重新排列字节为单位),以便容纳英特尔微处理器。如果子表检索已成功完成,结果将被复制到调用方的缓冲区中。如果未成功完成,该代码未修改用户的缓冲区中,并可以安全地返回失败。通过将设置为零字节需要参数,它未能复制字节的缓冲区,可以区分这种失败的缓冲区空间不足,可以指示代码示例。

一旦得到了 Unicode 子表,使用它可以检索字符码的标志符号索引,或者为了实现许多其他有用的功能。

将 Unicode 字符代码转换到标志符号索引的示例代码的GetTTUnicodeGlyphIndex函数中完成:

USHORT GetTTUnicodeGlyphIndex (
    HDC hdc,        // DC with a TrueType font selected.
    USHORT ch       // Unicode character to convert to Index.
    )

此函数具有较简单的界面,要求仅将句柄的 DC,hdc ;其中包含的 true Type 字体和 Unicode 字符代码转换,频道。成功时,该函数将返回 ch 的标志符号索引。如果 Unicode 字符代码不位于编码 (即,某个文件夹下没有没有标志符号) 返回缺失标志符号索引值为零。

首先,它通过分配缓冲区,并调用GetTTUnicodeCoverage函数检索 Unicode 子表。如果发生错误,示例代码无法通过返回缺失标志符号索引进行调用。在这种情况下,故障可能意味着 DC 不包含 TrueType 字体,或者 TrueType 字体不包含适当的 Unicode 子表。

接下来,该代码将尝试查找 Unicode 字符代码中的编码。每个格式 4 子表引用 TrueType 规范的"cmap"数据类型章中执行搜索。FindFormat4Segment函数进行线性搜索子表中的代码段。如果任何代码段不方括号此字符代码,然后字体文件不包含编码,因此没有任何标志符号。该代码然后返回缺失标志符号的索引。

GetTTUnicodeGlyphIndex函数的最后一个部分的标志符号索引查找。有两种方法来查找特定的标志符号的标志符号索引。这两种情况下使用 idRangeOffset 阵列,通过检查在其中找到的字符代码段的序号索引处的值。

第一种情况下,位于段 idRangeOffset 数组的索引处的值为零,如果代码取消引用具有相同的数组索引 idDelta 数组中,并将转换为使用取模运算的标志符号索引:

// Per TT spec, if the RangeOffset is zero,
    if ( idRangeOffset[iSegment] == 0)
    {
        // calculate the glyph index directly.
        GlyphIndex = (idDelta[iSegment] + ch) % 65536;
    }
    else
    {
     ...
    }
			

第二种情况下,位于段的序号索引处的值是索引的标志符号索引用于查找表中的一部分。根据订单和各个子表的数组位置,隐蔽索引这一轮,在 idRangeOffset 元素中使用的值的地址返回中间的 ID 值。TrueType 规范格式 4 子表章介绍了索引的机制。如果非零值,此值然后添加到 idDelta 的值并转换使用取模运算 ;否则为没有任何标志符号和返回缺失标志符号索引:

// Per TT spec, if the RangeOffset is zero,
    if ( idRangeOffset[iSegment] == 0)
    {
     ...
    }
    else
    {
        // otherwise, use the glyph ID array to get the index.
        USHORT idResult;    //Intermediate ID calc.

        idResult = *(
            idRangeOffset[iSegment]/2 + 
            (ch - startCount[iSegment]) + 
            &idRangeOffset[iSegment]
            );  // Indexing equation from TT spec.
        if (idResult)
            // Per TT spec, nonzero means there is a glyph.
            GlyphIndex = (idDelta[iSegment] + idResult) % 65536;
        else
            // Otherwise, return the missing glyph.
            GlyphIndex = 0;
    }

其他有用的函数可从 Unicode 子表进行解码。例如,此示例代码实现时调用的函数:

USHORT GetTTUnicodeCharCount ( 
    HDC hdc
    )

此函数会添加格式 4 子表,以查找 Unicode 字符编码 TrueType 字体文件中列出的总数中的每个段介绍的字符组成。但是请注意此函数必须测试每个单个字符代码的映射,如果涉及一段使用的标志符号 ID 数组,而不是连续的。

这也是指导您注意的不是是一定的标志符号的字体文件中包含的数字等效的 Unicode 字符编码映射到一个标志符号的计数。如果 Unicode 编码映射到同一个标志符号的多个字符代码,则可能存在更少的标志符号。也可能是多个字形字体文件中的映射建议比。例如: TrueType 打开 (现在称为 OpenType 版式) 表定义标志符号索引替换到多个备用标志符号。

GetTTUnicodeGlyphIndex函数还可用于实现的函数,以确定给定的 TrueType 字体中是否包含给定的 Unicode 字符代码的标志符号。只需调用GetTTUnicodeGlyphIndex函数中使用的字符代码并测试缺失标志符号索引 (值为 0) 与等同的返回。

关于实施

此示例代码编写为清晰的解释。它不非常适合重复使用,因为它在分配和检索 TrueType 表每次调用时的公共函数。用于实际应用程序中,是一种很好的优化缓存的 Unicode 编码的 TrueType 字体文件,只要它仍然在 DC 中。应用程序可以比较选入 DC 的字体相同的 TrueType 字体文件缓存,并将字体文件的校验和值进行比较。此校验和位于 TrueType 字体文件,该文件的开头的表目录中,并可以通过使用GetFontData函数来检索。请参见"表目录"下查找字体文件的校验和数据类型章 TrueType 规范的讨论。

完整的源代码
#pragma pack(1)     // for byte alignment
// We need byte alignment to be structure compatible with the
// contents of a TrueType font file

// Macros to swap from Big Endian to Little Endian
#define SWAPWORD(x) MAKEWORD( \ 
    HIBYTE(x), \ 
    LOBYTE(x) \ 
    )
#define SWAPLONG(x) MAKELONG( \ 
    SWAPWORD(HIWORD(x)), \ 
    SWAPWORD(LOWORD(x)) \ 
    )


typedef struct _CMap4   // From the TrueType Spec. revision 1.66
{
    USHORT format;          // Format number is set to 4. 
    USHORT length;          // Length in bytes. 
    USHORT version;         // Version number (starts at 0).
    USHORT segCountX2;      // 2 x segCount.
    USHORT searchRange;     // 2 x (2**floor(log2(segCount)))
    USHORT entrySelector;   // log2(searchRange/2)
    USHORT rangeShift;      // 2 x segCount - searchRange

    USHORT Arrays[1];       // Placeholder symbol for address of arrays following
} CMAP4, *LPCMAP4;


/*  CMAP table Data
    From the TrueType Spec revision 1.66

    USHORT  Table Version #
    USHORT  Number of encoding tables
*/ 
#define     CMAPHEADERSIZE  (sizeof(USHORT)*2)


/*  ENCODING entry Data aka CMAPENCODING
    From the TrueType Spec revision 1.66

    USHORT  Platform Id
    USHORT  Platform Specific Encoding Id
    ULONG   Byte Offset from beginning of table
*/ 
#define     ENCODINGSIZE    (sizeof(USHORT)*2 + sizeof(ULONG))

typedef struct _CMapEncoding
{
    USHORT  PlatformId;
    USHORT  EncodingId;
    ULONG   Offset;
} CMAPENCODING;


// Macro to pack a TrueType table name into a DWORD
#define     MAKETABLENAME(ch1, ch2, ch3, ch4) (\ 
    (((DWORD)(ch4)) << 24) | \ 
    (((DWORD)(ch3)) << 16) | \ 
    (((DWORD)(ch2)) << 8) | \ 
    ((DWORD)(ch1)) \ 
    )

/* public functions */ 
USHORT GetTTUnicodeGlyphIndex(HDC hdc, USHORT ch);
USHORT GetTTUnicodeCharCount(HDC hdc);


// DWORD packed four letter table name for each GetFontData()
// function call when working with the CMAP TrueType table
DWORD dwCmapName = MAKETABLENAME( 'c','m','a','p' );

USHORT *GetEndCountArray(LPBYTE pBuff)
{
    return (USHORT *)(pBuff + 7 * sizeof(USHORT));  // Per TT spec
}

USHORT *GetStartCountArray(LPBYTE pBuff)
{
    DWORD   segCount = ((LPCMAP4)pBuff)->segCountX2/2;
    return (USHORT *)( pBuff + 
        8 * sizeof(USHORT) +        // 7 header + 1 reserved USHORT
        segCount*sizeof(USHORT) );  // Per TT spec
}

USHORT *GetIdDeltaArray(LPBYTE pBuff)
{
    DWORD   segCount = ((LPCMAP4)pBuff)->segCountX2/2;
    return (USHORT *)( pBuff + 
        8 * sizeof(USHORT) +        // 7 header + 1 reserved USHORT
        segCount * 2 * sizeof(USHORT) );    // Per TT spec
}

USHORT *GetIdRangeOffsetArray(LPBYTE pBuff)
{
    DWORD   segCount = ((LPCMAP4)pBuff)->segCountX2/2;
    return (USHORT *)( pBuff + 
        8 * sizeof(USHORT) +        // 7 header + 1 reserved USHORT
        segCount * 3 * sizeof(USHORT) );    // Per TT spec
}


void SwapArrays( LPCMAP4 pFormat4 )
{
    DWORD   segCount = pFormat4->segCountX2/2;  // Per TT Spec
    DWORD   i;
    USHORT  *pGlyphId, 
            *pEndOfBuffer, 
            *pstartCount    = GetStartCountArray( (LPBYTE)pFormat4 ), 
            *pidDelta       = GetIdDeltaArray( (LPBYTE)pFormat4 ), 
            *pidRangeOffset = GetIdRangeOffsetArray( (LPBYTE)pFormat4 ), 
            *pendCount      = GetEndCountArray( (LPBYTE)pFormat4 );

    // Swap the array elements for Intel.
    for (i=0; i < segCount; i++)
    {
        pendCount[i] = SWAPWORD(pendCount[i]);
        pstartCount[i] = SWAPWORD(pstartCount[i]);
        pidDelta[i] = SWAPWORD(pidDelta[i]);
        pidRangeOffset[i] = SWAPWORD(pidRangeOffset[i]);
    }

    // Swap the Glyph Id array
    pGlyphId = pidRangeOffset + segCount;   // Per TT spec
    pEndOfBuffer = (USHORT*)((LPBYTE)pFormat4 + pFormat4->length);
    for (;pGlyphId < pEndOfBuffer; pGlyphId++)
    {
        *pGlyphId = SWAPWORD(*pGlyphId);
    }
} /* end of function SwapArrays */ 


BOOL GetFontEncoding ( 
    HDC hdc, 
    CMAPENCODING * pEncoding, 
    int iEncoding 
    )
/*
    Note for this function to work correctly, structures must 
    have byte alignment.
*/ 
{
    DWORD   dwResult;
    BOOL    fSuccess = TRUE;

    // Get the structure data from the TrueType font
    dwResult = GetFontData ( 
        hdc, 
        dwCmapName, 
        CMAPHEADERSIZE + ENCODINGSIZE*iEncoding, 
        pEncoding, 
        sizeof(CMAPENCODING) );
    fSuccess = (dwResult == sizeof(CMAPENCODING));

    // swap the Platform Id for Intel
    pEncoding->PlatformId = SWAPWORD(pEncoding->PlatformId);

    // swap the Specific Id for Intel
    pEncoding->EncodingId = SWAPWORD(pEncoding->EncodingId);

    // swap the subtable offset for Intel
    pEncoding->Offset = SWAPLONG(pEncoding->Offset);

    return fSuccess;

} /* end of function GetFontEncoding */ 

BOOL GetFontFormat4Header ( 
    HDC hdc, 
    LPCMAP4 pFormat4, 
    DWORD dwOffset 
    )
/*
    Note for this function to work correctly, structures must 
    have byte alignment.
*/ 
{
    BOOL    fSuccess = TRUE;
    DWORD   dwResult;
    int     i;
    USHORT  *pField;

    // Loop and Alias a writeable pointer to the field of interest
    pField = (USHORT *)pFormat4;

    for (i=0; i < 7; i++)
    {
        // Get the field from the subtable
        dwResult = GetFontData ( 

            hdc, 
            dwCmapName, 
            dwOffset + sizeof(USHORT)*i, 
            pField, 
            sizeof(USHORT) );
        
        // swap it to make it right for Intel.
        *pField = SWAPWORD(*pField);
        // move on to the next
        pField++;
        // accumulate our success
        fSuccess = (dwResult == sizeof(USHORT)) && fSuccess;
    }

    return fSuccess;

} /* end of function GetFontFormat4Header */ 

BOOL GetFontFormat4Subtable ( 
    HDC hdc,                    // DC with TrueType font
    LPCMAP4 pFormat4Subtable,   // destination buffer
    DWORD   dwOffset            // Offset within font
    )
{
    DWORD   dwResult;
    USHORT  length;
    

    // Retrieve the header values in swapped order
    if (!GetFontFormat4Header ( hdc, 
        pFormat4Subtable, 
        dwOffset ))
    {
        return FALSE;
    }

    // Get the rest of the table
    length = pFormat4Subtable->length - (7 * sizeof(USHORT));
    dwResult = GetFontData( hdc, 
        dwCmapName,
        dwOffset + 7 * sizeof(USHORT),      // pos of arrays
        (LPBYTE)pFormat4Subtable->Arrays,   // destination
        length );       

    if ( dwResult != length)
    {
        // We really shouldn't ever get here
        return FALSE;
    }
    
    // Swamp the arrays
    SwapArrays( pFormat4Subtable );

    return TRUE;
}

USHORT GetFontFormat4CharCount (
    LPCMAP4 pFormat4    // pointer to a valid Format4 subtable
    )
{
    USHORT  i,
            *pendCount = GetEndCountArray((LPBYTE) pFormat4),
            *pstartCount = GetStartCountArray((LPBYTE) pFormat4),
            *idRangeOffset = GetIdRangeOffsetArray( (LPBYTE) pFormat4 );

    // Count the # of glyphs
    USHORT nGlyphs = 0;

    if ( pFormat4 == NULL )
        return 0;

    // by adding up the coverage of each segment
    for (i=0; i < (pFormat4->segCountX2/2); i++)
    {

        if ( idRangeOffset[i] == 0)
        {
            // if per the TT spec, the idRangeOffset element is zero,
            // all of the characters in this segment exist.
            nGlyphs += pendCount[i] - pstartCount[i] +1;
        }
        else
        {
            // otherwise we have to test for glyph existence for
            // each character in the segment.
            USHORT idResult;    //Intermediate id calc.
            USHORT ch;

            for (ch = pstartCount[i]; ch <= pendCount[i]; ch++)
            {
                // determine if a glyph exists
                idResult = *(
                    idRangeOffset[i]/2 + 
                    (ch - pstartCount[i]) + 
                    &idRangeOffset[i]
                    );  // indexing equation from TT spec
                if (idResult != 0)
                    // Yep, count it.
                    nGlyphs++;
            }
        }
    }

    return nGlyphs;
} /* end of function GetFontFormat4CharCount */ 

BOOL GetTTUnicodeCoverage ( 
    HDC hdc,            // DC with TT font
    LPCMAP4 pBuffer,    // Properly allocated buffer
    DWORD cbSize,       // Size of properly allocated buffer
    DWORD *pcbNeeded    // size of buffer needed
    )
/*
    if cbSize is to small or zero, or if pBuffer is NULL the function
    will fail and return the required buffer size in *pcbNeeded.

    if another error occurs, the function will fail and *pcbNeeded will
    be zero.

    When the function succeeds, *pcbNeeded contains the number of bytes 
    copied to pBuffer.
*/ 
{
    USHORT          nEncodings;     // # of encoding in the TT font
    CMAPENCODING    Encoding;       // The current encoding
    DWORD           dwResult;
    DWORD           i, 
                    iUnicode;       // The Unicode encoding
    CMAP4           Format4;        // Unicode subtable format
    LPCMAP4         pFormat4Subtable;   // Working buffer for subtable

    // Get the number of subtables in the CMAP table from the CMAP header
    // The # of subtables is the second USHORT in the CMAP table, per the TT Spec.
    dwResult = GetFontData ( hdc, dwCmapName, sizeof(USHORT), &nEncodings, sizeof(USHORT) );
    nEncodings = SWAPWORD(nEncodings);
    
    if ( dwResult != sizeof(USHORT) )
    {
        // Something is wrong, we probably got GDI_ERROR back
        // Probably this means that the Device Context does not have
        // a TrueType font selected into it.
        return FALSE;
    }

    // Get the encodings and look for a Unicode Encoding
    iUnicode = nEncodings;
    for (i=0; i < nEncodings; i++)
    {
        // Get the encoding entry for each encoding
        if (!GetFontEncoding ( hdc, &Encoding, i ))
        {
            *pcbNeeded = 0;
            return FALSE;
        }
        
        // Take note of the Unicode encoding.
        // 
        // A Unicode encoding per the TrueType specification has a
        // Platform Id of 3 and a Platform specific encoding id of 1
        // Note that Symbol fonts are supposed to have a Platform Id of 3 
        // and a specific id of 0. If the TrueType spec. suggestions were
        // followed then the Symbol font's Format 4 encoding could also
        // be considered Unicode because the mapping would be in the
        // Private Use Area of Unicode. We assume this here and allow 
        // Symbol fonts to be interpreted. If they do not contain a 
        // Format 4, we bail later. If they do not have a Unicode 
        // character mapping, we'll get wrong results.
        // Code could infer from the coverage whether 3-0 fonts are 
        // Unicode or not by examining the segments for placement within
        // the Private Use Area Subrange.
        if (Encoding.PlatformId == 3 && 
            (Encoding.EncodingId == 1 || Encoding.EncodingId == 0) )
        {
            iUnicode = i;       // Set the index to the Unicode encoding
        }
    }

    // index out of range means failure to find a Unicode mapping
    if (iUnicode >= nEncodings)
    {
        // No Unicode encoding found.
        *pcbNeeded = 0;
        return FALSE;
    }

    // Get the header entries(first 7 USHORTs) for the Unicode encoding.
    if ( !GetFontFormat4Header ( hdc, &Format4, Encoding.Offset ) )
    {
        *pcbNeeded = 0;
        return FALSE;
    }

    // Check to see if we retrieved a Format 4 table 
    if ( Format4.format != 4 )
    {
        // Bad, subtable is not format 4, bail.
        // This could happen if the font is corrupt
        // It could also happen if there is a new font format we
        // don't understand.
        *pcbNeeded = 0;
        return FALSE;
    }

    // Figure buffer size and tell caller if buffer to small
    *pcbNeeded = Format4.length;    
    if (*pcbNeeded > cbSize || pBuffer == NULL)
    {
        // Either test indicates caller needs to know
        // the buffer size and the parameters are not setup
        // to continue.
        return FALSE;
    }

    // allocate a full working buffer
    pFormat4Subtable = (LPCMAP4)malloc ( Format4.length );
    if ( pFormat4Subtable == NULL)
    {
        // Bad things happening if we can't allocate memory
        *pcbNeeded = 0;
        return FALSE;
    }

    // get the entire subtable
    if (!GetFontFormat4Subtable ( hdc, pFormat4Subtable, Encoding.Offset ))
    {
        // Bad things happening if we can't allocate memory
        *pcbNeeded = 0;
        return FALSE;
    }

    // Copy the retrieved table into the buffer
    CopyMemory( pBuffer, 
        pFormat4Subtable, 
        pFormat4Subtable->length );

    free ( pFormat4Subtable );
    return TRUE;
} /* end of function GetTTUnicodeCoverage */ 

BOOL FindFormat4Segment (
    LPCMAP4 pTable,     // a valid Format4 subtable buffer
    USHORT ch,          // Unicode character to search for
    USHORT *piSeg       // out: index of segment containing ch
    )
/*
    if the Unicode character ch is not contained in one of the 
    segments the function returns FALSE.

    if the Unicode character ch is found in a segment, the index
    of the segment is placed in*piSeg and the function returns
    TRUE.
*/ 
{
    USHORT  i, 
            segCount = pTable->segCountX2/2;
    USHORT  *pendCount = GetEndCountArray((LPBYTE) pTable);
    USHORT  *pstartCount = GetStartCountArray((LPBYTE) pTable);

    // Find segment that could contain the Unicode character code
    for (i=0; i < segCount && pendCount[i] < ch; i++);

    // We looked in them all, ch not there
    if (i >= segCount)
        return FALSE;
    
    // character code not within the range of the segment
    if (pstartCount[i] > ch)
        return FALSE;

    // this segment contains the character code
    *piSeg = i;
    return TRUE;
} /* end of function FindFormat4Segment */ 

USHORT GetTTUnicodeCharCount ( 
    HDC hdc
    )
/*
    Returns the number of Unicode character glyphs that 
    are in the TrueType font that is selected into the hdc.
*/ 
{
    LPCMAP4 pUnicodeCMapTable;
    USHORT  cChar;
    DWORD   dwSize;

    // Get the Unicode CMAP table from the TT font
    GetTTUnicodeCoverage( hdc, NULL, 0, &dwSize );
    pUnicodeCMapTable = (LPCMAP4)malloc( dwSize );
    if (!GetTTUnicodeCoverage( hdc, pUnicodeCMapTable, dwSize, &dwSize ))
    {
        // possibly no Unicode cmap, not a TT font selected,...
        free( pUnicodeCMapTable );
        return 0;
    }

    cChar = GetFontFormat4CharCount( pUnicodeCMapTable );
    free( pUnicodeCMapTable );

    return cChar;
} /* end of function GetTTUnicodeCharCount */ 


USHORT GetTTUnicodeGlyphIndex (
    HDC hdc,        // DC with a TrueType font selected
    USHORT ch       // Unicode character to convert to Index
    )
/*
    When the TrueType font contains a glyph for ch, the
    function returns the glyph index for that character.

    If an error occurs, or there is no glyph for ch, the
    function will return the missing glyph index of zero.
*/ 
{
    LPCMAP4 pUnicodeCMapTable;
    DWORD   dwSize;
    USHORT  iSegment;
    USHORT  *idRangeOffset;
    USHORT  *idDelta;
    USHORT  *startCount;
    USHORT  GlyphIndex = 0;     // Initialize to missing glyph

    // How big a buffer do we need for Unicode CMAP?
    GetTTUnicodeCoverage( hdc, NULL, 0, &dwSize );
    pUnicodeCMapTable = (LPCMAP4)malloc( dwSize );
    if (!GetTTUnicodeCoverage( hdc, pUnicodeCMapTable, dwSize, &dwSize ))
    {
        // Either no Unicode cmap, or some other error occurred
        // like font in DC is not TT.
        free( pUnicodeCMapTable );
        return 0;       // return missing glyph on error
    }

    // Find the cmap segment that has the character code.
    if (!FindFormat4Segment( pUnicodeCMapTable, ch, &iSegment ))
    {
        free( pUnicodeCMapTable );
        return 0;       // ch not in cmap, return missing glyph
    }

    // Get pointers to the cmap data
    idRangeOffset = GetIdRangeOffsetArray( (LPBYTE) pUnicodeCMapTable );
    idDelta = GetIdDeltaArray( (LPBYTE) pUnicodeCMapTable );
    startCount = GetStartCountArray( (LPBYTE) pUnicodeCMapTable );
    
    // Per TT spec, if the RangeOffset is zero,
    if ( idRangeOffset[iSegment] == 0)
    {
        // calculate the glyph index directly
        GlyphIndex = (idDelta[iSegment] + ch) % 65536;
    }
    else
    {
        // otherwise, use the glyph id array to get the index
        USHORT idResult;    //Intermediate id calc.

        idResult = *(
            idRangeOffset[iSegment]/2 + 
            (ch - startCount[iSegment]) + 
            &idRangeOffset[iSegment]
            );  // indexing equation from TT spec
        if (idResult)
            // Per TT spec, nonzero means there is a glyph
            GlyphIndex = (idDelta[iSegment] + idResult) % 65536;
        else
            // otherwise, return the missing glyph
            GlyphIndex = 0;
    }

    free( pUnicodeCMapTable );
    return GlyphIndex;
} /* end of function GetTTUnicodeGlyphIndex */ 

参考:
在 Unicode 标准的详细信息,请参阅:
   
   
Unicode 协会。 Unicode 标准 2.0 版。 阅读麻萨诸塞州艾迪逊 Wesley 开发人员按,1996年。 ISBN 0-201-48345-9。

在 internet 上: Unicode 协会 (http://www.unicode.org)
有关其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
210341 在 Windows 95 和 Windows 98 中的信息: Unicode 支持
TrueType 规范的详细信息,请参阅:
Microsoft TrueType Specifications (http://www.microsoft.com/typography/tt/tt.htm)

Microsoft TrueType 规范 (http://www.microsoft.com/typography/tt/tt.htm)

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值