Working with 'name' table | .NET

TrueType font table ’name’ is storage for text strings related to that font. These strings can be written in different languages and can represent various entities, such as font names, family names, designer names, license info, copyright notices, and so on. In short, the lines that are held in the table ’name’ describe font metadata.

Schema of ’name’ table.

The detailed specification for ’name’ table is accessible in Microsoft and Apple documentations.

There are 2 formats for a ’name’ table with numbers 0 and 1 correspondingly. In terms of the ’name’ table, these format numbers are named as version, so format 0 is designated as version 0, and format 1 - as version 1.

Format 1 differs from format 0 in language identification. Any entry in the ’name’ table has a language identifier, which is used to detect the language of that string. And the difference between format 0 and format 1 is in how these language identifiers are interpreted.

Language identifiers for format 0 have platform-specific interpretation, but language identifiers for format 1 are associated with language-tag strings, which identify languages regardless of the platform.

For more accuracy, format 1 allows keeping language identifiers of both types - identifiers with platform-specific interpretation, and identifiers associated with language-tag strings(i.e independent from a platform).

Aspose.Font library supports format 0 of the ’name’ table. Support for format 1 is planned for future releases.

Independently from the format that the ’name’ table has, any entry in this table is based on a particular component - the structure NameRecord.

The four main parameters of this structure are :

Parameters platformID, platformSpecificID, and languageID are used to set the language of the string in a platform-specific manner. Values of the parameters platformSpecificID, and languageID matter only in the context of the platformID parameter.

For example, platformSpecificID equal 0 defines Roman script for the Mac platform, and at the same time, platformSpecificID defines Roman script for the Windows platform. In a similar way the value of languageID matters only in the context of the used platformID parameter.

For example languageID defining English USA equals 0 for platformID = 1(Mac) and 0x0409 for platformID = 3(Windows).

The exclusions only are languageID for the name table format 1, associated with a language-tag string, which identifies languages regardless of the platform.

Parameter nameID is a number, which identifies a logical string category, such as font name, family name, and others. There is a predefined set of name identifiers, which is the same for all platforms and languages.

So, each entry of the table ’name’ conditionally can be divided into 3 parts:

  1. logical string category,
  2. string language,
  3. the string itself.

Parameter nameID is related to the first part, parameters platformID, platformSpecificID and languageID are related to the second part.

How to work with ’name’ table records using Aspose.Font?

Support for the ’name’ table is provided by class TtfNameTable. Further, we consider the functionality of this object.

First, let’s describe the enumerations needed to work with the functionality of TtfNameTable class.

As it was mentioned above, “Values of the parameters platformSpecificID, and languageID matter only in the context of the platformID parameter.” So, when platformID is 0, and this defines the Unicode platform, use UnicodePlatformSpecificId enumeration, when platformID is 1 (Macintosh platform), use MacPlatformSpecificId enumeration, and when platformID is 3 (Windows platform), use MSPlatformSpecificId enumeration.

Enumerations MSLanguageId and MacLanguageId are related to languageID parameter. Use MSLanguageId enumeration, when platformID is 3 (Windows platform) and use MacLanguageId enumeration when platformID is 1 (Macintosh platform).

Let’s now proceed with the matter of getting and refreshing entries from the ’name table.

How to get records from the ’name’ table?

Let’s start from the method GetAllNameRecords(). This method, as follows from its name, returns all the entries without exclusions of the ’name’ table. In practice the method is not often called as users in most cases do not need all the entries, so to get the needed entry the list of entries has to be thoroughly filtered.

The matter is that even in one logic category, FontFamily for example, the string data of this category can be in different languages. So each language needs a separate entry in the ’name’ table for this logic category. Like if the data for the FontFamily category exists in English, French, and German the FontFamily category would include 3 entries.

Furthermore, the language entry can be itself divided into a few entries that coincide by string data values and languageID value, but differ by values of platformID and platformSpecificID parameters.

To simplify the data sampling from the ’name’ table Aspose.Font library offers the next methods:

How to add/update records in the ’name’ table?

Class TtfNameTable provides a method AddName to add or update records in the ’name’ table. This method creates the structure of the type NameRecord and inserts it into the ’name’ table. If the record coincides with the added one by parameters platformID, platformSpecificID, languageID, and nameID already exists, the method doesn’t add a new record, but updates string data in the existing record using the value, defined by parameter name.

Parameter nameId defines the logical string category for a record. Parameters platformId, platformSpecificId, and languageId are used to set the language of the string. And the last parameter name is used to set string data for a record.

Examples of using functions of the TtfNameTable object.

Include using for following namespaces:

1using System;
2using System.Text;
3using System.Collections.Generic;
4using Aspose.Font.Ttf;
5using Aspose.Font.TtfTables;

Declaring and initializing a font variable.

1TtfFont font;

Next 2 snippets print value for category Full font name and produce same result for font Lora-Regular

1    //1
2    string fullFontName = font.TtfTables.NameTable.GetNameById(TtfNameTable.NameId.FullName);
3    Console.WriteLine(String.Format("Full font name: {0}", fullFontName ));
4    //2
5    string fullFontName = font.TtfTables.NameTable.GetMultiLanguageNameById(TtfNameTable.NameId.FullName).GetEnglishString();
6    Console.WriteLine(String.Format("Full font name: {0}", fullFontName));

Printing the whole content of the ’name’ table.

The snippet below shows how to fulfill this operation.

 1    TtfNameTable.NameId[] ids = Enum.GetValues<TtfNameTable.NameId>();
 3    foreach (TtfNameTable.NameId nameId in ids)
 4    {
 5        MultiLanguageString mlString = font.TtfTables.NameTable.GetMultiLanguageNameById(nameId);
 6            if (mlString == null)
 7                continue;
 8        Console.WriteLine(string.Format("{0}: {1}", nameId, GetMultiLanguageStringValue(mlString)));
 9    }
10    //Using of this method has no sense when strings from 'name' table have only single language, but it can be useful when font
11    //'name' table include multilingual strings
12    string GetMultiLanguageStringValue(MultiLanguageString mlString)
13    {
14        int[] languages = mlString.GetAllLanguageIds();
15        if(languages.Length == 1)
16            return mlString.GetEnglishString();
18        StringBuilder sb = new StringBuilder();
20        for(int i = 0; i < languages.Length; i++) 
21        {
22            int langId = languages[i];
23            sb.Append(String.Format("{0}: {1}", Enum.GetName<TtfNameTable.MSLanguageId>(
24                (TtfNameTable.MSLanguageId)langId), mlString.GetStringForLanguageId(langId)));
25            if (i != (languages.Length - 1))
26                sb.Append(", ");
27        }
29        return sb.ToString();
30    }		

Updating values for categories “Font Subfamily name” and “Description”

To add or refresh the entry in the table ’name’ correctly, we need to pass the values of the platformID, platformSpecificID and languageID parameters that coincide with those that are already present in the ’name’ table. For this, before refreshing the data we will read the existing records of the NameRecord type relevant to the refreshing logic category, defined by name identifier.

 1    //Struct for update operations
 2    struct UpdateData
 3    {
 4        private TtfNameTable.NameId _nameId;
 5        private string _data;
 7        public UpdateData(TtfNameTable.NameId nameId, string data)
 8        {
 9            this._nameId = nameId;
10            this._data = data;
11        }
13        public TtfNameTable.NameId NameId => this._nameId;
14        public string StringData => this._data;
15    }
17    UpdateData[] recordsToUpdate = new UpdateData[]
18    {
19        new UpdateData(TtfNameTable.NameId.FontSubfamily, "Italic"),
20        new UpdateData(TtfNameTable.NameId.Description, "New description")
21    };
23    TtfNameTable.NameRecord firstRecord = null;
25    foreach (UpdateData updateData in recordsToUpdate)
26    {
27        TtfNameTable.NameRecord[] records = font.TtfTables.NameTable.GetNameRecordsByNameId(updateData.NameId);
29        //Declare variable for NameRecord structure to use for update operations
30        TtfNameTable.NameRecord record = null;
32        //In this example we will use only info from the first NameRecord structure returned to update font metadata.
33        //Many actual fonts require serious analyze of all NameRecords returned to update metadata correctly
35        //Initialize just created variables
36        if (records.Length == 0)
37        {
38            //If no any record was found for current name identifer,
39            //we will use first found record for any name identifier
40            if (firstRecord == null)
41            {
42                firstRecord = GetFirstExistingRecord(font.TtfTables.NameTable);
43            }
44            record = firstRecord;
45        }
46        else
47        {
48            record = records[0];
49        }
51        //Add or update record in 'name' table
52        font.TtfTables.NameTable.AddName(updateData.NameId, (TtfNameTable.PlatformId)record PlatformId, 
53                        record.PlatformSpecificId, record.LanguageId, updateData.StringData);
54    }		
57    TtfNameTable.NameRecord GetFirstExistingRecord(TtfNameTable table)
58    {
59        TtfNameTable.NameRecord[] records = null;
60        foreach (TtfNameTable.NameId nameId in Enum.GetValues<TtfNameTable.NameId>())
61        {
62            records = table.GetNameRecordsByNameId(nameId);
63            if (records.Length != 0)
64                return records[0];
65        }
67        return table.GetAllNameRecords()[0];
68    } 	

Other examples for refreshing the ’name’ table you can find in the test solution MetadataExamples.cs.

Subscribe to Aspose Product Updates

Get monthly newsletters & offers directly delivered to your mailbox.