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.
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:
- platform identifier(platformID),
- platform-specific identifier(platformSpecificID),
- name identifier (nameID),
- and language identifier (languageID).
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:
- logical string category,
- string language,
- 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.
- Enumerations NameID and PlatformId are related to such parameters described above as nameID and platformID.
- Enumerations UnicodePlatformSpecificId, MacPlatformSpecificId, MSPlatformSpecificId are related to parameter platformSpecificID.
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:
- GetNameRecordsByNameId()- returns the list of entries for the set by the user logic category, defined by the
nameID
parameter. - GetMultiLanguageNameById() - returns all the entries, relevant to the passed logic category nameID as an object of MultiLanguageString type. By means of the object of - MultiLanguageString type, we can find out all the languages of this category and get the string data for the set language. You can get the list of all the languages by calling the GetAllLanguageIds() method of the MultiLanguageString type. After receiving the list of the languages we can call the GetStringForLanguageId() method for each languageID. This method returns the data string written in this language.
MultiLanguageString Class also offers the next methods:
- ContainsString (string str)- checks whether a passed string is present inside all the language strings of the object.
- GetEnglishString() - returns a string written in English if found. It returns the first string, languageID of which is MSLanguageId.English_United_States, MSLanguageId.English_Australia, MSLanguageId.English_United_Kingdom, MSLanguageId.English_Canada, or MSLanguageId.English_New_Zealand. If there are no strings with the relevant language identifier, the method returns the first string of the list.
- GetAllStrings() - returns all the strings of all languages which the object includes.
- The simplest to use method of class TtfNameTable is GetNameById(), which was designed for cases when you need only to get the value for the set category in English. This method looks for a record, which is corresponding to 2 criteria:
- This record is written in English, so it has the value MSLanguageId.English_United_States or MSLanguageId.English_United_Kingdom for the languageID parameter.
- This record has platformID with the value equal to FontEnvironment.Current.CurrentPlatformId (3 in current implementation, which declares Microsoft platform).
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>();
2
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();
17
18 StringBuilder sb = new StringBuilder();
19
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 }
28
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;
6
7 public UpdateData(TtfNameTable.NameId nameId, string data)
8 {
9 this._nameId = nameId;
10 this._data = data;
11 }
12
13 public TtfNameTable.NameId NameId => this._nameId;
14 public string StringData => this._data;
15 }
16
17 UpdateData[] recordsToUpdate = new UpdateData[]
18 {
19 new UpdateData(TtfNameTable.NameId.FontSubfamily, "Italic"),
20 new UpdateData(TtfNameTable.NameId.Description, "New description")
21 };
22
23 TtfNameTable.NameRecord firstRecord = null;
24
25 foreach (UpdateData updateData in recordsToUpdate)
26 {
27 TtfNameTable.NameRecord[] records = font.TtfTables.NameTable.GetNameRecordsByNameId(updateData.NameId);
28
29 //Declare variable for NameRecord structure to use for update operations
30 TtfNameTable.NameRecord record = null;
31
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
34
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 }
50
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 }
55
56
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 }
66
67 return table.GetAllNameRecords()[0];
68 }
Other examples for refreshing the ’name’ table you can find in the test solution MetadataExamples.cs.