1// SPDX-License-Identifier: MIT OR MPL-2.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later
2// Copyright 2010, SIL International, All rights reserved.
3
4#include "inc/Main.h"
5#include "inc/Endian.h"
6
7#include "inc/NameTable.h"
8#include "inc/UtfCodec.h"
9
10using namespace graphite2;
11
12NameTable::NameTable(const void* data, size_t length, uint16 platformId, uint16 encodingID)
13 : m_platformId(0), m_encodingId(0), m_languageCount(0),
14 m_platformOffset(0), m_platformLastRecord(0), m_nameDataLength(0),
15 m_table(0), m_nameData(NULL)
16{
17 void *pdata = gralloc<byte>(length);
18 if (!pdata) return;
19 memcpy(pdata, data, length);
20 m_table = reinterpret_cast<const TtfUtil::Sfnt::FontNames*>(pdata);
21
22 if ((length > sizeof(TtfUtil::Sfnt::FontNames)) &&
23 (length > sizeof(TtfUtil::Sfnt::FontNames) +
24 sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap<uint16>(m_table->count) - 1)))
25 {
26 uint16 offset = be::swap<uint16>(m_table->string_offset);
27 if (offset < length)
28 {
29 m_nameData = reinterpret_cast<const uint8*>(pdata) + offset;
30 setPlatformEncoding(platformId, encodingID);
31 m_nameDataLength = uint16(length - offset);
32 return;
33 }
34 }
35 free(const_cast<TtfUtil::Sfnt::FontNames*>(m_table));
36 m_table = NULL;
37}
38
39uint16 NameTable::setPlatformEncoding(uint16 platformId, uint16 encodingID)
40{
41 if (!m_nameData) return 0;
42 uint16 i = 0;
43 uint16 count = be::swap<uint16>(m_table->count);
44 for (; i < count; i++)
45 {
46 if (be::swap<uint16>(m_table->name_record[i].platform_id) == platformId &&
47 be::swap<uint16>(m_table->name_record[i].platform_specific_id) == encodingID)
48 {
49 m_platformOffset = i;
50 break;
51 }
52 }
53 while ((++i < count) &&
54 (be::swap<uint16>(m_table->name_record[i].platform_id) == platformId) &&
55 (be::swap<uint16>(m_table->name_record[i].platform_specific_id) == encodingID))
56 {
57 m_platformLastRecord = i;
58 }
59 m_encodingId = encodingID;
60 m_platformId = platformId;
61 return 0;
62}
63
64void* NameTable::getName(uint16& languageId, uint16 nameId, gr_encform enc, uint32& length)
65{
66 uint16 anyLang = 0;
67 uint16 enUSLang = 0;
68 uint16 bestLang = 0;
69 if (!m_table)
70 {
71 languageId = 0;
72 length = 0;
73 return NULL;
74 }
75 for (uint16 i = m_platformOffset; i <= m_platformLastRecord; i++)
76 {
77 if (be::swap<uint16>(m_table->name_record[i].name_id) == nameId)
78 {
79 uint16 langId = be::swap<uint16>(m_table->name_record[i].language_id);
80 if (langId == languageId)
81 {
82 bestLang = i;
83 break;
84 }
85 // MS language tags have the language in the lower byte, region in the higher
86 else if ((langId & 0xFF) == (languageId & 0xFF))
87 {
88 bestLang = i;
89 }
90 else if (langId == 0x409)
91 {
92 enUSLang = i;
93 }
94 else
95 {
96 anyLang = i;
97 }
98 }
99 }
100 if (!bestLang)
101 {
102 if (enUSLang) bestLang = enUSLang;
103 else
104 {
105 bestLang = anyLang;
106 if (!anyLang)
107 {
108 languageId = 0;
109 length = 0;
110 return NULL;
111 }
112 }
113 }
114 const TtfUtil::Sfnt::NameRecord & nameRecord = m_table->name_record[bestLang];
115 languageId = be::swap<uint16>(nameRecord.language_id);
116 uint16 utf16Length = be::swap<uint16>(nameRecord.length);
117 uint16 offset = be::swap<uint16>(nameRecord.offset);
118 if(offset + utf16Length > m_nameDataLength)
119 {
120 languageId = 0;
121 length = 0;
122 return NULL;
123 }
124 utf16Length >>= 1; // in utf16 units
125 utf16::codeunit_t * utf16Name = gralloc<utf16::codeunit_t>(utf16Length + 1);
126 if (!utf16Name)
127 {
128 languageId = 0;
129 length = 0;
130 return NULL;
131 }
132 const uint8* pName = m_nameData + offset;
133 for (size_t i = 0; i < utf16Length; i++)
134 {
135 utf16Name[i] = be::read<uint16>(pName);
136 }
137 utf16Name[utf16Length] = 0;
138 if (!utf16::validate(utf16Name, utf16Name + utf16Length))
139 {
140 free(utf16Name);
141 languageId = 0;
142 length = 0;
143 return NULL;
144 }
145 switch (enc)
146 {
147 case gr_utf8:
148 {
149 utf8::codeunit_t* uniBuffer = gralloc<utf8::codeunit_t>(3 * utf16Length + 1);
150 if (!uniBuffer)
151 {
152 free(utf16Name);
153 languageId = 0;
154 length = 0;
155 return NULL;
156 }
157 utf8::iterator d = uniBuffer;
158 for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d)
159 *d = *s;
160 length = uint32(d - uniBuffer);
161 uniBuffer[length] = 0;
162 free(utf16Name);
163 return uniBuffer;
164 }
165 case gr_utf16:
166 length = utf16Length;
167 return utf16Name;
168 case gr_utf32:
169 {
170 utf32::codeunit_t * uniBuffer = gralloc<utf32::codeunit_t>(utf16Length + 1);
171 if (!uniBuffer)
172 {
173 free(utf16Name);
174 languageId = 0;
175 length = 0;
176 return NULL;
177 }
178 utf32::iterator d = uniBuffer;
179 for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d)
180 *d = *s;
181 length = uint32(d - uniBuffer);
182 uniBuffer[length] = 0;
183 free(utf16Name);
184 return uniBuffer;
185 }
186 }
187 free(utf16Name);
188 languageId = 0;
189 length = 0;
190 return NULL;
191}
192
193uint16 NameTable::getLanguageId(const char * bcp47Locale)
194{
195 size_t localeLength = strlen(bcp47Locale);
196 uint16 localeId = m_locale2Lang.getMsId(bcp47Locale);
197 if (m_table && (be::swap<uint16>(m_table->format) == 1))
198 {
199 const uint8 * pLangEntries = reinterpret_cast<const uint8*>(m_table) +
200 sizeof(TtfUtil::Sfnt::FontNames)
201 + sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap<uint16>(m_table->count) - 1);
202 uint16 numLangEntries = be::read<uint16>(pLangEntries);
203 const TtfUtil::Sfnt::LangTagRecord * langTag =
204 reinterpret_cast<const TtfUtil::Sfnt::LangTagRecord*>(pLangEntries);
205 if (pLangEntries + numLangEntries * sizeof(TtfUtil::Sfnt::LangTagRecord) <= m_nameData)
206 {
207 for (uint16 i = 0; i < numLangEntries; i++)
208 {
209 uint16 offset = be::swap<uint16>(langTag[i].offset);
210 uint16 length = be::swap<uint16>(langTag[i].length);
211 if ((offset + length <= m_nameDataLength) && (length == 2 * localeLength))
212 {
213 const uint8* pName = m_nameData + offset;
214 bool match = true;
215 for (size_t j = 0; j < localeLength; j++)
216 {
217 uint16 code = be::read<uint16>(pName);
218 if ((code > 0x7F) || (code != bcp47Locale[j]))
219 {
220 match = false;
221 break;
222 }
223 }
224 if (match)
225 return 0x8000 + i;
226 }
227 }
228 }
229 }
230 return localeId;
231}
232