/* Copyright (C) 2026 Carl Hetherington This file is part of libttf. libttf is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. libttf is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libttf. If not, see . */ #include "name_table.h" #include "tags.h" #include "util.h" #include using namespace ttf; NameTable::NameTable(Source& source, int header_index) : Table(source, header_index) { Source table_source(source, _offset); _format = table_source.get_uint16(); auto const count = table_source.get_uint16(); auto const string_offset = table_source.get_uint16(); for (auto i = 0; i < count; ++i) { auto string_source = Source(source, _offset + string_offset); _name_records.push_back(NameRecord(table_source, string_source, i)); } std::sort(_name_records.begin(), _name_records.end(), [](NameRecord const& a, NameRecord const& b) { return a.original_offset < b.original_offset; }); } void NameTable::write(Sink& header_sink, Sink&& data_sink) const { { auto const string_offset = static_cast(6 + _name_records.size() * 12); SubSink string_sink(data_sink, string_offset); data_sink.put(_format); data_sink.put(static_cast(_name_records.size())); data_sink.put(string_offset); auto const name_records = data_sink.offset(); for (auto name: _name_records) { data_sink.set_offset(name_records + name.header_index * 12); name.write(data_sink, string_sink); } string_sink.pad(); } header_sink.put(NAME); header_sink.put(data_sink.checksum(data_sink.initial_offset())); header_sink.put(data_sink.initial_offset()); header_sink.put(data_sink.offset_from_initial()); } /** @param string_offset Offset of the string table within the font */ NameTable::NameRecord::NameRecord(Source& table_source, Source& string_source, int header_index) : header_index(header_index) , platform_id(table_source.get_uint16()) , platform_specific_id(table_source.get_uint16()) , language_id(table_source.get_uint16()) , name_id(table_source.get_uint16()) { auto const length = table_source.get_uint16(); original_offset = table_source.get_uint16(); switch (platform_id) { case 1: /* Macintosh */ name = string_source.get_string(original_offset, length); break; case 0: /* Unicode (16BE) */ case 3: /* Microsoft */ { name = string_source.get_wstring(original_offset, length); break; } default: throw UnsupportedPlatformIdentifier(platform_id); }; } NameTable::NameRecord::NameRecord(int header_index_, uint16_t platform_id_, uint16_t platform_specific_id_, uint16_t language_id_, Identifier name_id_, std::string name_) : header_index(header_index_) , platform_id(platform_id_) , platform_specific_id(platform_specific_id_) , language_id(language_id_) , name_id(static_cast(name_id_)) , name(name_) { } NameTable::NameRecord::NameRecord(int header_index_, uint16_t platform_id_, uint16_t platform_specific_id_, uint16_t language_id_, Identifier name_id_, std::wstring name_) : header_index(header_index_) , platform_id(platform_id_) , platform_specific_id(platform_specific_id_) , language_id(language_id_) , name_id(static_cast(name_id_)) , name(name_) { } std::vector NameTable::names(NameTable::Identifier id) const { std::vector result; for (auto const& record: _name_records) { if (record.name_id == static_cast(id)) { result.push_back(record.name); } } return result; } void NameTable::set_names(NameTable::Identifier id, std::string const& name, std::wstring const& wname) { for (auto& record: _name_records) { if (record.name_id == static_cast(id)) { if (record.name.index() == 0) { record.name = name; } else { record.name = wname; } } } } void NameTable::NameRecord::write(Sink& table_sink, Sink& string_sink) const { table_sink.put(platform_id); table_sink.put(platform_specific_id); table_sink.put(language_id); table_sink.put(name_id); if (auto* str = boost::variant2::get_if(&name)) { table_sink.put(static_cast(str->length())); table_sink.put(static_cast(string_sink.offset_from_initial())); string_sink.put(*str); } else if (auto* wstr = boost::variant2::get_if(&name)) { table_sink.put(static_cast(wstr->length() * 2)); table_sink.put(static_cast(string_sink.offset_from_initial())); string_sink.put(*wstr); } }