1
//! Utilities for obtaining a name from a fonts `name` table.
2

            
3
use crate::binary::read::ReadScope;
4
use crate::error::ParseError;
5
use crate::tables::NameTable;
6
use encoding_rs::{DecoderResult, MACINTOSH, UTF_16BE};
7
use std::ffi::CString;
8

            
9
pub fn fontcode_get_name(
10
    name_table_data: &[u8],
11
    name_id: u16,
12
) -> Result<Option<CString>, ParseError> {
13
    let name_table = ReadScope::new(name_table_data).read::<NameTable<'_>>()?;
14
    let mut best = 0;
15
    let mut result = None;
16
    for name_record in &name_table.name_records {
17
        if name_record.name_id == name_id {
18
            let offset = usize::from(name_record.offset);
19
            let length = usize::from(name_record.length);
20
            let name_data = name_table
21
                .string_storage
22
                .offset_length(offset, length)?
23
                .data();
24
            if let Some((score, encoding)) = score_encoding(
25
                name_record.platform_id,
26
                name_record.encoding_id,
27
                name_record.language_id,
28
            ) {
29
                if best < score {
30
                    if let Some(name) = decode_name(encoding, name_data) {
31
                        result = Some(name);
32
                        best = score;
33
                    }
34
                }
35
            }
36
        }
37
    }
38
    Ok(result)
39
}
40

            
41
enum NameEncoding {
42
    Utf16Be,
43
    AppleRoman,
44
}
45

            
46
fn score_encoding(
47
    platform_id: u16,
48
    encoding_id: u16,
49
    language_id: u16,
50
) -> Option<(usize, NameEncoding)> {
51
    match (platform_id, encoding_id, language_id) {
52
        // Windows; Unicode full repertoire
53
        (3, 10, _) => Some((1000, NameEncoding::Utf16Be)),
54

            
55
        // Unicode; Unicode full repertoire
56
        (0, 6, 0) => Some((900, NameEncoding::Utf16Be)),
57

            
58
        // Unicode; Unicode 2.0 and onwards semantics, Unicode full repertoire
59
        (0, 4, 0) => Some((800, NameEncoding::Utf16Be)),
60

            
61
        // Windows; Unicode BMP
62
        (3, 1, 0x409) => Some((750, NameEncoding::Utf16Be)),
63
        (3, 1, lang) if lang != 0x409 => Some((700, NameEncoding::Utf16Be)),
64

            
65
        // Unicode; Unicode 2.0 and onwards semantics, Unicode BMP only
66
        (0, 3, 0) => Some((600, NameEncoding::Utf16Be)),
67

            
68
        // Unicode; ISO/IEC 10646 semantics
69
        (0, 2, 0) => Some((500, NameEncoding::Utf16Be)),
70

            
71
        // Unicode; Unicode 1.1 semantics
72
        (0, 1, 0) => Some((400, NameEncoding::Utf16Be)),
73

            
74
        // Unicode; Unicode 1.0 semantics
75
        (0, 0, 0) => Some((300, NameEncoding::Utf16Be)),
76

            
77
        // Windows, Symbol
78
        (3, 0, _) => Some((200, NameEncoding::Utf16Be)),
79

            
80
        // Apple Roman
81
        (1, 0, 0) => Some((150, NameEncoding::AppleRoman)),
82
        (1, 0, lang) if lang != 0 => Some((100, NameEncoding::AppleRoman)),
83
        _ => None,
84
    }
85
}
86

            
87
fn decode_name(encoding: NameEncoding, data: &[u8]) -> Option<CString> {
88
    let mut decoder = match encoding {
89
        NameEncoding::Utf16Be => UTF_16BE.new_decoder(),
90
        NameEncoding::AppleRoman => MACINTOSH.new_decoder(),
91
    };
92
    if let Some(size) = decoder.max_utf8_buffer_length(data.len()) {
93
        let mut s = String::with_capacity(size);
94
        let (res, _read) = decoder.decode_to_string_without_replacement(data, &mut s, true);
95
        match res {
96
            DecoderResult::InputEmpty => CString::new(s).ok(),
97
            DecoderResult::OutputFull => None, // should not happen
98
            DecoderResult::Malformed(_, _) => None,
99
        }
100
    } else {
101
        None
102
    }
103
}