michael@0: /* GRAPHITE2 LICENSING michael@0: michael@0: Copyright 2010, SIL International michael@0: All rights reserved. michael@0: michael@0: This library is free software; you can redistribute it and/or modify michael@0: it under the terms of the GNU Lesser General Public License as published michael@0: by the Free Software Foundation; either version 2.1 of License, or michael@0: (at your option) any later version. michael@0: michael@0: This program is distributed in the hope that it will be useful, michael@0: but WITHOUT ANY WARRANTY; without even the implied warranty of michael@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU michael@0: Lesser General Public License for more details. michael@0: michael@0: You should also have received a copy of the GNU Lesser General Public michael@0: License along with this library in the file named "LICENSE". michael@0: If not, write to the Free Software Foundation, 51 Franklin Street, michael@0: Suite 500, Boston, MA 02110-1335, USA or visit their web page on the michael@0: internet at http://www.fsf.org/licenses/lgpl.html. michael@0: michael@0: Alternatively, the contents of this file may be used under the terms of the michael@0: Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public michael@0: License, as published by the Free Software Foundation, either version 2 michael@0: of the License or (at your option) any later version. michael@0: */ michael@0: #include "inc/Main.h" michael@0: #include "inc/Endian.h" michael@0: michael@0: #include "inc/NameTable.h" michael@0: #include "inc/UtfCodec.h" michael@0: michael@0: using namespace graphite2; michael@0: michael@0: NameTable::NameTable(const void* data, size_t length, uint16 platformId, uint16 encodingID) michael@0: : m_platformId(0), m_encodingId(0), m_languageCount(0), michael@0: m_platformOffset(0), m_platformLastRecord(0), m_nameDataLength(0), michael@0: m_table(0), m_nameData(NULL) michael@0: { michael@0: void *pdata = gralloc(length); michael@0: if (!pdata) return; michael@0: memcpy(pdata, data, length); michael@0: m_table = reinterpret_cast(pdata); michael@0: michael@0: if ((length > sizeof(TtfUtil::Sfnt::FontNames)) && michael@0: (length > sizeof(TtfUtil::Sfnt::FontNames) + michael@0: sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap(m_table->count) - 1))) michael@0: { michael@0: uint16 offset = be::swap(m_table->string_offset); michael@0: m_nameData = reinterpret_cast(pdata) + offset; michael@0: setPlatformEncoding(platformId, encodingID); michael@0: m_nameDataLength = length - offset; michael@0: } michael@0: else michael@0: { michael@0: free(const_cast(m_table)); michael@0: m_table = NULL; michael@0: } michael@0: } michael@0: michael@0: uint16 NameTable::setPlatformEncoding(uint16 platformId, uint16 encodingID) michael@0: { michael@0: if (!m_nameData) return 0; michael@0: uint16 i = 0; michael@0: uint16 count = be::swap(m_table->count); michael@0: for (; i < count; i++) michael@0: { michael@0: if (be::swap(m_table->name_record[i].platform_id) == platformId && michael@0: be::swap(m_table->name_record[i].platform_specific_id) == encodingID) michael@0: { michael@0: m_platformOffset = i; michael@0: break; michael@0: } michael@0: } michael@0: while ((++i < count) && michael@0: (be::swap(m_table->name_record[i].platform_id) == platformId) && michael@0: (be::swap(m_table->name_record[i].platform_specific_id) == encodingID)) michael@0: { michael@0: m_platformLastRecord = i; michael@0: } michael@0: m_encodingId = encodingID; michael@0: m_platformId = platformId; michael@0: return 0; michael@0: } michael@0: michael@0: void* NameTable::getName(uint16& languageId, uint16 nameId, gr_encform enc, uint32& length) michael@0: { michael@0: uint16 anyLang = 0; michael@0: uint16 enUSLang = 0; michael@0: uint16 bestLang = 0; michael@0: if (!m_table) michael@0: { michael@0: languageId = 0; michael@0: length = 0; michael@0: return NULL; michael@0: } michael@0: for (uint16 i = m_platformOffset; i <= m_platformLastRecord; i++) michael@0: { michael@0: if (be::swap(m_table->name_record[i].name_id) == nameId) michael@0: { michael@0: uint16 langId = be::swap(m_table->name_record[i].language_id); michael@0: if (langId == languageId) michael@0: { michael@0: bestLang = i; michael@0: break; michael@0: } michael@0: // MS language tags have the language in the lower byte, region in the higher michael@0: else if ((langId & 0xFF) == (languageId & 0xFF)) michael@0: { michael@0: bestLang = i; michael@0: } michael@0: else if (langId == 0x409) michael@0: { michael@0: enUSLang = i; michael@0: } michael@0: else michael@0: { michael@0: anyLang = i; michael@0: } michael@0: } michael@0: } michael@0: if (!bestLang) michael@0: { michael@0: if (enUSLang) bestLang = enUSLang; michael@0: else michael@0: { michael@0: bestLang = anyLang; michael@0: if (!anyLang) michael@0: { michael@0: languageId = 0; michael@0: length = 0; michael@0: return NULL; michael@0: } michael@0: } michael@0: } michael@0: const TtfUtil::Sfnt::NameRecord & nameRecord = m_table->name_record[bestLang]; michael@0: languageId = be::swap(nameRecord.language_id); michael@0: uint16 utf16Length = be::swap(nameRecord.length); michael@0: uint16 offset = be::swap(nameRecord.offset); michael@0: if(offset + utf16Length > m_nameDataLength) michael@0: { michael@0: languageId = 0; michael@0: length = 0; michael@0: return NULL; michael@0: } michael@0: utf16Length >>= 1; // in utf16 units michael@0: utf16::codeunit_t * utf16Name = gralloc(utf16Length); michael@0: if (!utf16Name) michael@0: { michael@0: languageId = 0; michael@0: length = 0; michael@0: return NULL; michael@0: } michael@0: const uint8* pName = m_nameData + offset; michael@0: for (size_t i = 0; i < utf16Length; i++) michael@0: { michael@0: utf16Name[i] = be::read(pName); michael@0: } michael@0: switch (enc) michael@0: { michael@0: case gr_utf8: michael@0: { michael@0: utf8::codeunit_t* uniBuffer = gralloc(3 * utf16Length + 1); michael@0: if (!uniBuffer) michael@0: { michael@0: free(utf16Name); michael@0: languageId = 0; michael@0: length = 0; michael@0: return NULL; michael@0: } michael@0: utf8::iterator d = uniBuffer; michael@0: for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d) michael@0: *d = *s; michael@0: length = d - uniBuffer; michael@0: uniBuffer[length] = 0; michael@0: free(utf16Name); michael@0: return uniBuffer; michael@0: } michael@0: case gr_utf16: michael@0: length = utf16Length; michael@0: return utf16Name; michael@0: case gr_utf32: michael@0: { michael@0: utf32::codeunit_t * uniBuffer = gralloc(utf16Length + 1); michael@0: if (!uniBuffer) michael@0: { michael@0: free(utf16Name); michael@0: languageId = 0; michael@0: length = 0; michael@0: return NULL; michael@0: } michael@0: utf32::iterator d = uniBuffer; michael@0: for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d) michael@0: *d = *s; michael@0: length = d - uniBuffer; michael@0: uniBuffer[length] = 0; michael@0: free(utf16Name); michael@0: return uniBuffer; michael@0: } michael@0: } michael@0: free(utf16Name); michael@0: languageId = 0; michael@0: length = 0; michael@0: return NULL; michael@0: } michael@0: michael@0: uint16 NameTable::getLanguageId(const char * bcp47Locale) michael@0: { michael@0: size_t localeLength = strlen(bcp47Locale); michael@0: uint16 localeId = m_locale2Lang.getMsId(bcp47Locale); michael@0: if (m_table && (be::swap(m_table->format) == 1)) michael@0: { michael@0: const uint8 * pLangEntries = reinterpret_cast(m_table) + michael@0: sizeof(TtfUtil::Sfnt::FontNames) michael@0: + sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap(m_table->count) - 1); michael@0: uint16 numLangEntries = be::read(pLangEntries); michael@0: const TtfUtil::Sfnt::LangTagRecord * langTag = michael@0: reinterpret_cast(pLangEntries); michael@0: if (pLangEntries + numLangEntries * sizeof(TtfUtil::Sfnt::LangTagRecord) <= m_nameData) michael@0: { michael@0: for (uint16 i = 0; i < numLangEntries; i++) michael@0: { michael@0: uint16 offset = be::swap(langTag[i].offset); michael@0: uint16 length = be::swap(langTag[i].length); michael@0: if ((offset + length <= m_nameDataLength) && (length == 2 * localeLength)) michael@0: { michael@0: const uint8* pName = m_nameData + offset; michael@0: bool match = true; michael@0: for (size_t j = 0; j < localeLength; j++) michael@0: { michael@0: uint16 code = be::read(pName); michael@0: if ((code > 0x7F) || (code != bcp47Locale[j])) michael@0: { michael@0: match = false; michael@0: break; michael@0: } michael@0: } michael@0: if (match) michael@0: return 0x8000 + i; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return localeId; michael@0: } michael@0: