package font // ============================================================================ // Font Names API // ============================================================================ // Compare UTF-8 string prefix against big-endian UTF-16 string // Returns number of UTF-8 bytes matched, or -1 on mismatch @(private) compare_utf8_to_utf16_bigendian_prefix :: proc(s1: [^]u8, len1: i32, s2: [^]u8, len2: i32) -> i32 { i: i32 = 0 l2 := len2 s2_off: i32 = 0 for l2 > 0 { ch := u16(s2[s2_off]) * 256 + u16(s2[s2_off + 1]) if ch < 0x80 { if i >= len1 do return -1 if s1[i] != u8(ch) do return -1 i += 1 } else if ch < 0x800 { if i + 1 >= len1 do return -1 if s1[i] != 0xc0 + u8(ch >> 6) do return -1 i += 1 if s1[i] != 0x80 + u8(ch & 0x3f) do return -1 i += 1 } else if ch >= 0xd800 && ch < 0xdc00 { // Surrogate pair ch2 := u16(s2[s2_off + 2]) * 256 + u16(s2[s2_off + 3]) if i + 3 >= len1 do return -1 c := (u32(ch - 0xd800) << 10) + u32(ch2 - 0xdc00) + 0x10000 if s1[i] != 0xf0 + u8(c >> 18) do return -1 i += 1 if s1[i] != 0x80 + u8((c >> 12) & 0x3f) do return -1 i += 1 if s1[i] != 0x80 + u8((c >> 6) & 0x3f) do return -1 i += 1 if s1[i] != 0x80 + u8(c & 0x3f) do return -1 i += 1 s2_off += 2 l2 -= 2 } else if ch >= 0xdc00 && ch < 0xe000 { return -1 // Invalid lone low surrogate } else { if i + 2 >= len1 do return -1 if s1[i] != 0xe0 + u8(ch >> 12) do return -1 i += 1 if s1[i] != 0x80 + u8((ch >> 6) & 0x3f) do return -1 i += 1 if s1[i] != 0x80 + u8(ch & 0x3f) do return -1 i += 1 } s2_off += 2 l2 -= 2 } return i } // Compare UTF-8 string to big-endian UTF-16 string // Returns true if they are equal compare_utf8_to_utf16_bigendian :: proc(s1: cstring, len1: i32, s2: cstring, len2: i32) -> bool { return compare_utf8_to_utf16_bigendian_prefix( transmute([^]u8)s1, len1, transmute([^]u8)s2, len2, ) == len1 } // Match font name against name table entries, handling family+subfamily pairs @(private) matchpair :: proc(fc: [^]u8, nm: u32, name: [^]u8, nlen: i32, target_id: i32, next_id: i32) -> bool { count := i32(ttUSHORT(fc[nm + 2:])) string_offset := nm + u32(ttUSHORT(fc[nm + 4:])) for i in 0 ..< count { loc := nm + 6 + u32(12 * i) id := i32(ttUSHORT(fc[loc + 6:])) if id == target_id { platform := i32(ttUSHORT(fc[loc + 0:])) encoding := i32(ttUSHORT(fc[loc + 2:])) language := i32(ttUSHORT(fc[loc + 4:])) // Is this a Unicode encoding? if platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10) { slen := i32(ttUSHORT(fc[loc + 8:])) off := i32(ttUSHORT(fc[loc + 10:])) // Check if there's a prefix match matchlen := compare_utf8_to_utf16_bigendian_prefix(name, nlen, fc[string_offset + u32(off):], slen) if matchlen >= 0 { // Check for target_id+1 immediately following, with same encoding & language if i + 1 < count && i32(ttUSHORT(fc[loc + 12 + 6:])) == next_id && i32(ttUSHORT(fc[loc + 12:])) == platform && i32(ttUSHORT(fc[loc + 12 + 2:])) == encoding && i32(ttUSHORT(fc[loc + 12 + 4:])) == language { slen = i32(ttUSHORT(fc[loc + 12 + 8:])) off = i32(ttUSHORT(fc[loc + 12 + 10:])) if slen == 0 { if matchlen == nlen { return true } } else if matchlen < nlen && name[matchlen] == ' ' { matchlen += 1 if compare_utf8_to_utf16_bigendian_prefix( name[matchlen:], nlen - matchlen, fc[string_offset + u32(off):], slen, ) == nlen - matchlen { return true } } } else { // If nothing immediately following if matchlen == nlen { return true } } } } } } return false } // Check if font matches name and style flags @(private) matches :: proc(fc: [^]u8, offset: u32, name: [^]u8, flags: i32) -> bool { // Get string length nlen: i32 = 0 for name[nlen] != 0 { nlen += 1 } if !is_font(fc[offset:]) do return false // Check italics/bold/underline flags in macStyle if flags != 0 { hd := find_table(fc, offset, "head") if (i32(ttUSHORT(fc[hd + 44:])) & 7) != (flags & 7) do return false } nm := find_table(fc, offset, "name") if nm == 0 do return false if flags != 0 { // If we checked the macStyle flags, then just check the family and ignore the subfamily if matchpair(fc, nm, name, nlen, 16, -1) do return true if matchpair(fc, nm, name, nlen, 1, -1) do return true if matchpair(fc, nm, name, nlen, 3, -1) do return true } else { if matchpair(fc, nm, name, nlen, 16, 17) do return true if matchpair(fc, nm, name, nlen, 1, 2) do return true if matchpair(fc, nm, name, nlen, 3, -1) do return true } return false } // Get font name string from name table // Returns pointer to string data (may be big-endian UTF-16) and sets length get_font_name_string :: proc( font: ^Font_Info, length: ^i32, platform_id, encoding_id, language_id, name_id: i32, ) -> cstring { fc := font.data offset := u32(font.fontstart) nm := find_table(fc, offset, "name") if nm == 0 do return nil count := i32(ttUSHORT(fc[nm + 2:])) string_offset := nm + u32(ttUSHORT(fc[nm + 4:])) for i in 0 ..< count { loc := nm + 6 + u32(12 * i) if platform_id == i32(ttUSHORT(fc[loc + 0:])) && encoding_id == i32(ttUSHORT(fc[loc + 2:])) && language_id == i32(ttUSHORT(fc[loc + 4:])) && name_id == i32(ttUSHORT(fc[loc + 6:])) { length^ = i32(ttUSHORT(fc[loc + 8:])) return cstring(&fc[string_offset + u32(ttUSHORT(fc[loc + 10:]))]) } } return nil } // Find font by name in a font collection // Returns font offset or -1 if not found find_matching_font :: proc(fontdata: [^]u8, name: cstring, flags: i32) -> i32 { for i: i32 = 0;; i += 1 { off := get_font_offset_for_index(fontdata, i) if off < 0 do return off if matches(fontdata, u32(off), transmute([^]u8)name, flags) { return off } } }