1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use crate::binary::read::ReadScope;
use crate::error::ParseError;
use crate::tables::NameTable;
use encoding_rs::{DecoderResult, MACINTOSH, UTF_16BE};
use std::ffi::CString;
pub fn fontcode_get_name(
name_table_data: &[u8],
name_id: u16,
) -> Result<Option<CString>, ParseError> {
let name_table = ReadScope::new(name_table_data).read::<NameTable<'_>>()?;
let mut best = 0;
let mut result = None;
for name_record in &name_table.name_records {
if name_record.name_id == name_id {
let offset = usize::from(name_record.offset);
let length = usize::from(name_record.length);
let name_data = name_table
.string_storage
.offset_length(offset, length)?
.data();
if let Some((score, encoding)) = score_encoding(
name_record.platform_id,
name_record.encoding_id,
name_record.language_id,
) {
if best < score {
if let Some(name) = decode_name(encoding, name_data) {
result = Some(name);
best = score;
}
}
}
}
}
Ok(result)
}
enum NameEncoding {
Utf16Be,
AppleRoman,
}
fn score_encoding(
platform_id: u16,
encoding_id: u16,
language_id: u16,
) -> Option<(usize, NameEncoding)> {
match (platform_id, encoding_id, language_id) {
(3, 10, _) => Some((1000, NameEncoding::Utf16Be)),
(0, 6, 0) => Some((900, NameEncoding::Utf16Be)),
(0, 4, 0) => Some((800, NameEncoding::Utf16Be)),
(3, 1, 0x409) => Some((750, NameEncoding::Utf16Be)),
(3, 1, lang) if lang != 0x409 => Some((700, NameEncoding::Utf16Be)),
(0, 3, 0) => Some((600, NameEncoding::Utf16Be)),
(0, 2, 0) => Some((500, NameEncoding::Utf16Be)),
(0, 1, 0) => Some((400, NameEncoding::Utf16Be)),
(0, 0, 0) => Some((300, NameEncoding::Utf16Be)),
(3, 0, _) => Some((200, NameEncoding::Utf16Be)),
(1, 0, 0) => Some((150, NameEncoding::AppleRoman)),
(1, 0, lang) if lang != 0 => Some((100, NameEncoding::AppleRoman)),
_ => None,
}
}
fn decode_name(encoding: NameEncoding, data: &[u8]) -> Option<CString> {
let mut decoder = match encoding {
NameEncoding::Utf16Be => UTF_16BE.new_decoder(),
NameEncoding::AppleRoman => MACINTOSH.new_decoder(),
};
if let Some(size) = decoder.max_utf8_buffer_length(data.len()) {
let mut s = String::with_capacity(size);
let (res, _read) = decoder.decode_to_string_without_replacement(data, &mut s, true);
match res {
DecoderResult::InputEmpty => CString::new(s).ok(),
DecoderResult::OutputFull => None,
DecoderResult::Malformed(_, _) => None,
}
} else {
None
}
}