TA的每日心情 | 慵懒 16 小时前 |
---|
签到天数: 1065 天 [LV.10]以坛为家III
星辰大海
- 积分
- 1357828
|
调教了一个下午GPT,终于调教出来了。.net原生方法获取Postscript name。支持TTF/OTF/TTC格式。
CFF比较复杂,建议不处理,只判断tag="name"已经足够。
此处也把CFF的判断代码贴一下,虽然用false给短路了,但在某种情况下可能也会有参考价值。
贴代码:
- using System;
- using System.IO;
- namespace TTFParser
- {
- class Program
- {
- static void Main(string[] args)
- {
- Console.Write("Input font file path:");
- string filePath = Console.ReadLine();
- // Read the file as binary data
- byte[] fileData = File.ReadAllBytes(filePath);
- if (fileData.Length >= 4 && fileData[0] == 0x74 && fileData[1] == 0x74 && fileData[2] == 0x63 && fileData[3] == 0x66)
- {
- // TTC file signature: 0x74 0x74 0x63 0x66 (ttcf)
- // Parse the TTC header
- int numFonts = ReadUInt32(fileData, 8);
- int offsetTableOffset = 12;
- for (int fontIndex = 0; fontIndex < numFonts; fontIndex++)
- {
- // Get the offset and length of the specified font
- int fontOffset = ReadUInt32(fileData, offsetTableOffset + (fontIndex * 4));
- FontAttributeAnalysis(fileData, fontOffset);
- }
- }
- else
- {
- // OTF file signature: 0x4F 0x54 0x54 0x4F (OTTO)
- // TTF file signature: 0x00 0x01 0x00 0x00
- FontAttributeAnalysis(fileData, 0);
- }
- }
- static void FontAttributeAnalysis(byte[] fileData, int tableInitOffset)
- {
- // Parse the font header
- int tableCount = ReadUInt16(fileData, tableInitOffset + 4);
- int tableOffset = tableInitOffset + 12;
- string familyName = null;
- string postscriptName = null;
- // Iterate through the tables to find the name table
- for (int i = 0; i < tableCount; i++)
- {
- int tagOffset = tableOffset + (i * 16);
- string tag = ReadString(fileData, tagOffset, 4);
- if (tag == "name")
- {
- int nameTableOffset = ReadUInt32(fileData, tagOffset + 8);
- int nameCount = ReadUInt16(fileData, nameTableOffset + 2);
- int stringOffset = ReadUInt16(fileData, nameTableOffset + 4);
- // Iterate through the name records to find family name and postscript name
- for (int j = 0; j < nameCount; j++)
- {
- int nameRecordOffset = nameTableOffset + 6 + (j * 12);
- int platformID = ReadUInt16(fileData, nameRecordOffset);
- int encodingID = ReadUInt16(fileData, nameRecordOffset + 2);
- int languageID = ReadUInt16(fileData, nameRecordOffset + 4);
- int nameID = ReadUInt16(fileData, nameRecordOffset + 6);
- int nameLength = ReadUInt16(fileData, nameRecordOffset + 8);
- int nameOffset = nameTableOffset + stringOffset + ReadUInt16(fileData, nameRecordOffset + 10);
- // 各类型ID对应字段的含义可以参考: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6name.html
- Console.WriteLine("[DEBUG] platformID={0} encodingID={1} languageID={2} nameID={3} value={4}",
- platformID, encodingID, languageID, nameID, ReadString(fileData, nameOffset, nameLength));
- if (nameID == 1) // Family name ID
- {
- if (familyName == null)
- {
- familyName = ReadString(fileData, nameOffset, nameLength);
- }
- }
- else if (nameID == 6) // Postscript name ID
- {
- if (postscriptName == null)
- {
- postscriptName = ReadString(fileData, nameOffset, nameLength);
- }
- }
- //if (familyName != null && postscriptName != null)
- //{
- // break;
- //}
- }
- break;
- }
- else if (tag == "CFF " && false) //先用false屏蔽,建议不处理
- {
- // CFF比较复杂,建议不处理,只判断tag="name"已经足够。这只是简单的例子,没有全覆盖,上面tag判断的字符串有个空格,要注意
- // Adobe的CFF规范 https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf
- // Parse the 'CFF ' table to find the Postscript name.
- // The CFF table format is complex and not covered in this simple example.
- // You need to understand the CFF specification to implement this function.
- // Here is a very simplified example:
- Console.WriteLine("[DEBUG] CCF running...");
- int cffTableOffset = ReadUInt32(fileData, tagOffset + 8);
- // The Name INDEX is located at offset 2 in the CFF table.
- int nameIndexOffset = cffTableOffset + 2;
- int nameCount = ReadUInt16(fileData, nameIndexOffset);
- // The Top DICT INDEX follows the Name INDEX.
- int dictIndexOffset = nameIndexOffset + 2 + nameCount * 2 + 1;
- for (int j = 0; j < nameCount; j++)
- {
- // Parse the Name INDEX to get the font name.
- int nameOffset = ReadUInt16(fileData, nameIndexOffset + 2 + j * 2);
- int nextNameOffset = ReadUInt16(fileData, nameIndexOffset + 2 + (j + 1) * 2);
- if (cffTableOffset + nameOffset < nextNameOffset - nameOffset)
- {
- string fontName = ReadString(fileData, cffTableOffset + nameOffset, nextNameOffset - nameOffset);
- Console.WriteLine("CCF Font Name: " + fontName);
- }
- // Parse the Top DICT INDEX to get the font dictionary.
- int dictOffset = ReadUInt16(fileData, dictIndexOffset + 2 + j * 2);
- int nextDictOffset = ReadUInt16(fileData, dictIndexOffset + 2 + (j + 1) * 2);
-
- for (int k = dictOffset; k < nextDictOffset; k++)
- {
- // The PostScript name is stored in the font dictionary with the operator 12 30.
- if (fileData[cffTableOffset + k] == 12 && fileData[cffTableOffset + k + 1] == 30)
- {
- // The PostScript name follows the operator and is encoded as a string.
- int postscriptNameLength = fileData[cffTableOffset + k + 2];
- postscriptName = ReadString(fileData, cffTableOffset + k + 3, postscriptNameLength);
- break;
- }
- // The Family name is stored in the font dictionary with the operator 12 38.
- if (fileData[cffTableOffset + k] == 12 && fileData[cffTableOffset + k + 1] == 38)
- {
- // The Family name follows the operator and is encoded as a string.
- int familyNameLength = fileData[cffTableOffset + k + 2];
- familyName = ReadString(fileData, cffTableOffset + k + 3, familyNameLength);
- break;
- }
- }
- }
- }
- }
- Console.WriteLine("Family Name: " + familyName);
- Console.WriteLine("Postscript Name: " + postscriptName);
- Console.WriteLine("=============================");
- }
- static int ReadUInt16(byte[] data, int offset)
- {
- return (data[offset] << 8) | data[offset + 1];
- }
- static int ReadUInt32(byte[] data, int offset)
- {
- return (data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3];
- }
- static string ReadString(byte[] data, int offset, int length)
- {
- return System.Text.Encoding.ASCII.GetString(data, offset, length);
- }
- }
- }
复制代码
|
|