//! Localized strings describing font names and other metadata.
//!
//! This provides higher level interfaces for accessing the data in the
//! OpenType [name](https://learn.microsoft.com/en-us/typography/opentype/spec/name)
//! table.
//!
//! # Example
//! The following function will print all localized strings from the set
//! of predefined identifiers in a font:
//! ```
//! use skrifa::{string::StringId, MetadataProvider};
//!
//! fn print_well_known_strings<'a>(font: &impl MetadataProvider<'a>) {
//! for id in StringId::predefined() {
//! let strings = font.localized_strings(id);
//! if strings.clone().next().is_some() {
//! println!("[{:?}]", id);
//! for string in font.localized_strings(id) {
//! println!("{:?} {}", string.language(), string.to_string());
//! }
//! }
//! }
//! }
//! ```
use read_fonts::{
tables::name::{CharIter, Name, NameRecord, NameString},
TableProvider,
};
use core::fmt;
#[doc(inline)]
pub use read_fonts::types::NameId as StringId;
/// Iterator over the characters of a string.
#[derive(Clone)]
pub struct Chars<'a> {
inner: Option<CharIter<'a>>,
}
impl<'a> Iterator for Chars<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
self.inner.as_mut()?.next()
}
}
/// Iterator over a collection of localized strings for a specific identifier.
#[derive(Clone)]
pub struct LocalizedStrings<'a> {
name: Option<Name<'a>>,
records: core::slice::Iter<'a, NameRecord>,
id: StringId,
}
impl<'a> LocalizedStrings<'a> {
/// Creates a new localized string iterator from the given font and string identifier.
pub fn new(font: &impl TableProvider<'a>, id: StringId) -> Self {
let name = font.name().ok();
let records = name
.as_ref()
.map(|name| name.name_record().iter())
.unwrap_or([].iter());
Self { name, records, id }
}
/// Returns the informational string identifier for this iterator.
pub fn id(&self) -> StringId {
self.id
}
/// Returns the best available English string or the first string in the sequence.
///
/// This prefers the following languages, in order: "en-US", "en",
/// "" (empty, for bare Unicode platform strings which don't have an associated
/// language).
///
/// If none of these are found, returns the first string, or `None` if the sequence
/// is empty.
pub fn english_or_first(self) -> Option<LocalizedString<'a>> {
let mut best_rank = -1;
let mut best_string = None;
for (i, string) in self.enumerate() {
let rank = match (i, string.language()) {
(_, Some("en-US")) => return Some(string),
(_, Some("en")) => 2,
(_, None) => 1,
(0, _) => 0,
_ => continue,
};
if rank > best_rank {
best_rank = rank;
best_string = Some(string);
}
}
best_string
}
}
impl<'a> Iterator for LocalizedStrings<'a> {
type Item = LocalizedString<'a>;
fn next(&mut self) -> Option<Self::Item> {
let name = self.name.as_ref()?;
loop {
let record = self.records.next()?;
if record.name_id() == self.id {
return Some(LocalizedString::new(name, record));
}
}
}
}
impl Default for LocalizedStrings<'_> {
fn default() -> Self {
Self {
name: None,
records: [].iter(),
id: StringId::default(),
}
}
}
/// String containing a name or other font metadata in a specific language.
#[derive(Clone, Debug)]
pub struct LocalizedString<'a> {
language: Option<Language>,
value: Option<NameString<'a>>,
}
impl<'a> LocalizedString<'a> {
fn new(name: &Name<'a>, record: &NameRecord) -> Self {
let language = Language::new(name, record);
let value = record.string(name.string_data()).ok();
Self { language, value }
}
/// Returns the BCP-47 language identifier for the localized string.
pub fn language(&self) -> Option<&str> {
self.language.as_ref().map(|language| language.as_str())
}
/// Returns an iterator over the characters of the localized string.
pub fn chars(&self) -> Chars<'a> {
Chars {
inner: self.value.map(|value| value.chars()),
}
}
}
impl fmt::Display for LocalizedString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for ch in self.chars() {
ch.fmt(f)?;
}
Ok(())
}
}
/// This value is chosen arbitrarily to accommodate common language tags that
/// are almost always <= 11 bytes (LLL-SSSS-RR where L is primary language, S
/// is script and R is region) and to keep the Language enum at a reasonable
/// 32 bytes in size.
const MAX_INLINE_LANGUAGE_LEN: usize = 30;
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
enum Language {
Inline {
buf: [u8; MAX_INLINE_LANGUAGE_LEN],
len: u8,
},
Static(&'static str),
}
impl Language {
fn new(name: &Name, record: &NameRecord) -> Option<Self> {
let language_id = record.language_id();
// For version 1 name tables, prefer language tags:
// https://learn.microsoft.com/en-us/typography/opentype/spec/name#naming-table-version-1
const BASE_LANGUAGE_TAG_ID: u16 = 0x8000;
if name.version() == 1 && language_id >= BASE_LANGUAGE_TAG_ID {
let index = (language_id - BASE_LANGUAGE_TAG_ID) as usize;
let language_string = name
.lang_tag_record()?
.get(index)?
.lang_tag(name.string_data())
.ok()?;
Self::from_name_string(&language_string)
} else {
match record.platform_id() {
// We only match Macintosh and Windows language ids.
1 | 3 => Self::from_language_id(language_id),
_ => None,
}
}
}
/// Decodes a language tag string into an inline ASCII byte sequence.
fn from_name_string(s: &NameString) -> Option<Self> {
let mut buf = [0u8; MAX_INLINE_LANGUAGE_LEN];
let mut len = 0;
for ch in s.chars() {
// From "Tags for Identifying Languages" <https://www.rfc-editor.org/rfc/rfc5646.html#page-6>:
// "Although [RFC5234] refers to octets, the language tags described in
// this document are sequences of characters from the US-ASCII [ISO646]
// repertoire"
// Therefore we assume that non-ASCII characters signal an invalid language tag.
if !ch.is_ascii() || len == MAX_INLINE_LANGUAGE_LEN {
return None;
}
buf[len] = ch as u8;
len += 1;
}
Some(Self::Inline {
buf,
len: len as u8,
})
}
fn from_language_id(language_id: u16) -> Option<Self> {
Some(Self::Static(language_id_to_bcp47(language_id)?))
}
fn as_str(&self) -> &str {
match self {
Self::Inline { buf: data, len } => {
let data = &data[..*len as usize];
core::str::from_utf8(data).unwrap_or_default()
}
Self::Static(str) => str,
}
}
}
/// Converts an OpenType language identifier to a BCP-47 language tag.
fn language_id_to_bcp47(language_id: u16) -> Option<&'static str> {
match LANGUAGE_ID_TO_BCP47.binary_search_by(|entry| entry.0.cmp(&language_id)) {
Ok(ix) => LANGUAGE_ID_TO_BCP47.get(ix).map(|entry| entry.1),
_ => None,
}
}
/// Mapping of OpenType name table language identifier to BCP-47 language tag.
/// Borrowed from Skia: <https://skia.googlesource.com/skia/+/refs/heads/main/src/sfnt/SkOTTable_name.cpp#98>
const LANGUAGE_ID_TO_BCP47: &[(u16, &str)] = &[
/* A mapping from Mac Language Designators to BCP 47 codes.
* The following list was constructed more or less manually.
* Apple now uses BCP 47 (post OSX10.4), so there will be no new entries.
*/
(0, "en"), //English
(1, "fr"), //French
(2, "de"), //German
(3, "it"), //Italian
(4, "nl"), //Dutch
(5, "sv"), //Swedish
(6, "es"), //Spanish
(7, "da"), //Danish
(8, "pt"), //Portuguese
(9, "nb"), //Norwegian
(10, "he"), //Hebrew
(11, "ja"), //Japanese
(12, "ar"), //Arabic
(13, "fi"), //Finnish
(14, "el"), //Greek
(15, "is"), //Icelandic
(16, "mt"), //Maltese
(17, "tr"), //Turkish
(18, "hr"), //Croatian
(19, "zh-Hant"), //Chinese (Traditional)
(20, "ur"), //Urdu
(21, "hi"), //Hindi
(22, "th"), //Thai
(23, "ko"), //Korean
(24, "lt"), //Lithuanian
(25, "pl"), //Polish
(26, "hu"), //Hungarian
(27, "et"), //Estonian
(28, "lv"), //Latvian
(29, "se"), //Sami
(30, "fo"), //Faroese
(31, "fa"), //Farsi (Persian)
(32, "ru"), //Russian
(33, "zh-Hans"), //Chinese (Simplified)
(34, "nl"), //Dutch
(35, "ga"), //Irish(Gaelic)
(36, "sq"), //Albanian
(37, "ro"), //Romanian
(38, "cs"), //Czech
(39, "sk"), //Slovak
(40, "sl"), //Slovenian
(41, "yi"), //Yiddish
(42, "sr"), //Serbian
(43, "mk"), //Macedonian
(44, "bg"), //Bulgarian
(45, "uk"), //Ukrainian
(46, "be"), //Byelorussian
(47, "uz"), //Uzbek
(48, "kk"), //Kazakh
(49, "az-Cyrl"), //Azerbaijani (Cyrillic)
(50, "az-Arab"), //Azerbaijani (Arabic)
(51, "hy"), //Armenian
(52, "ka"), //Georgian
(53, "mo"), //Moldavian
(54, "ky"), //Kirghiz
(55, "tg"), //Tajiki
(56, "tk"), //Turkmen
(57, "mn-Mong"), //Mongolian (Traditional)
(58, "mn-Cyrl"), //Mongolian (Cyrillic)
(59, "ps"), //Pashto
(60, "ku"), //Kurdish
(61, "ks"), //Kashmiri
(62, "sd"), //Sindhi
(63, "bo"), //Tibetan
(64, "ne"), //Nepali
(65, "sa"), //Sanskrit
(66, "mr"), //Marathi
(67, "bn"), //Bengali
(68, "as"), //Assamese
(69, "gu"), //Gujarati
(70, "pa"), //Punjabi
(71, "or"), //Oriya
(72, "ml"), //Malayalam
(73, "kn"), //Kannada
(74, "ta"), //Tamil
(75, "te"), //Telugu
(76, "si"), //Sinhalese
(77, "my"), //Burmese
(78, "km"), //Khmer
(79, "lo"), //Lao
(80, "vi"), //Vietnamese
(81, "id"), //Indonesian
(82, "tl"), //Tagalog
(83, "ms-Latn"), //Malay (Roman)
(84, "ms-Arab"), //Malay (Arabic)
(85, "am"), //Amharic
(86, "ti"), //Tigrinya
(87, "om"), //Oromo
(88, "so"), //Somali
(89, "sw"), //Swahili
(90, "rw"), //Kinyarwanda/Ruanda
(91, "rn"), //Rundi
(92, "ny"), //Nyanja/Chewa
(93, "mg"), //Malagasy
(94, "eo"), //Esperanto
(128, "cy"), //Welsh
(129, "eu"), //Basque
(130, "ca"), //Catalan
(131, "la"), //Latin
(132, "qu"), //Quechua
(133, "gn"), //Guarani
(134, "ay"), //Aymara
(135, "tt"), //Tatar
(136, "ug"), //Uighur
(137, "dz"), //Dzongkha
(138, "jv-Latn"), //Javanese (Roman)
(139, "su-Latn"), //Sundanese (Roman)
(140, "gl"), //Galician
(141, "af"), //Afrikaans
(142, "br"), //Breton
(143, "iu"), //Inuktitut
(144, "gd"), //Scottish (Gaelic)
(145, "gv"), //Manx (Gaelic)
(146, "ga"), //Irish (Gaelic with Lenition)
(147, "to"), //Tongan
(148, "el"), //Greek (Polytonic) Note: ISO 15924 does not have an equivalent script name.
(149, "kl"), //Greenlandic
(150, "az-Latn"), //Azerbaijani (Roman)
(151, "nn"), //Nynorsk
/* A mapping from Windows LCID to BCP 47 codes.
* This list is the sorted, curated output of tools/win_lcid.cpp.
* Note that these are sorted by value for quick binary lookup, and not logically by lsb.
* The 'bare' language ids (e.g. 0x0001 for Arabic) are omitted
* as they do not appear as valid language ids in the OpenType specification.
*/
(0x0401, "ar-SA"), //Arabic
(0x0402, "bg-BG"), //Bulgarian
(0x0403, "ca-ES"), //Catalan
(0x0404, "zh-TW"), //Chinese (Traditional)
(0x0405, "cs-CZ"), //Czech
(0x0406, "da-DK"), //Danish
(0x0407, "de-DE"), //German
(0x0408, "el-GR"), //Greek
(0x0409, "en-US"), //English
(0x040a, "es-ES_tradnl"), //Spanish
(0x040b, "fi-FI"), //Finnish
(0x040c, "fr-FR"), //French
(0x040d, "he-IL"), //Hebrew
(0x040d, "he"), //Hebrew
(0x040e, "hu-HU"), //Hungarian
(0x040e, "hu"), //Hungarian
(0x040f, "is-IS"), //Icelandic
(0x0410, "it-IT"), //Italian
(0x0411, "ja-JP"), //Japanese
(0x0412, "ko-KR"), //Korean
(0x0413, "nl-NL"), //Dutch
(0x0414, "nb-NO"), //Norwegian (Bokmål)
(0x0415, "pl-PL"), //Polish
(0x0416, "pt-BR"), //Portuguese
(0x0417, "rm-CH"), //Romansh
(0x0418, "ro-RO"), //Romanian
(0x0419, "ru-RU"), //Russian
(0x041a, "hr-HR"), //Croatian
(0x041b, "sk-SK"), //Slovak
(0x041c, "sq-AL"), //Albanian
(0x041d, "sv-SE"), //Swedish
(0x041e, "th-TH"), //Thai
(0x041f, "tr-TR"), //Turkish
(0x0420, "ur-PK"), //Urdu
(0x0421, "id-ID"), //Indonesian
(0x0422, "uk-UA"), //Ukrainian
(0x0423, "be-BY"), //Belarusian
(0x0424, "sl-SI"), //Slovenian
(0x0425, "et-EE"), //Estonian
(0x0426, "lv-LV"), //Latvian
(0x0427, "lt-LT"), //Lithuanian
(0x0428, "tg-Cyrl-TJ"), //Tajik (Cyrillic)
(0x0429, "fa-IR"), //Persian
(0x042a, "vi-VN"), //Vietnamese
(0x042b, "hy-AM"), //Armenian
(0x042c, "az-Latn-AZ"), //Azeri (Latin)
(0x042d, "eu-ES"), //Basque
(0x042e, "hsb-DE"), //Upper Sorbian
(0x042f, "mk-MK"), //Macedonian (FYROM)
(0x0432, "tn-ZA"), //Setswana
(0x0434, "xh-ZA"), //isiXhosa
(0x0435, "zu-ZA"), //isiZulu
(0x0436, "af-ZA"), //Afrikaans
(0x0437, "ka-GE"), //Georgian
(0x0438, "fo-FO"), //Faroese
(0x0439, "hi-IN"), //Hindi
(0x043a, "mt-MT"), //Maltese
(0x043b, "se-NO"), //Sami (Northern)
(0x043e, "ms-MY"), //Malay
(0x043f, "kk-KZ"), //Kazakh
(0x0440, "ky-KG"), //Kyrgyz
(0x0441, "sw-KE"), //Kiswahili
(0x0442, "tk-TM"), //Turkmen
(0x0443, "uz-Latn-UZ"), //Uzbek (Latin)
(0x0443, "uz"), //Uzbek
(0x0444, "tt-RU"), //Tatar
(0x0445, "bn-IN"), //Bengali
(0x0446, "pa-IN"), //Punjabi
(0x0447, "gu-IN"), //Gujarati
(0x0448, "or-IN"), //Oriya
(0x0449, "ta-IN"), //Tamil
(0x044a, "te-IN"), //Telugu
(0x044b, "kn-IN"), //Kannada
(0x044c, "ml-IN"), //Malayalam
(0x044d, "as-IN"), //Assamese
(0x044e, "mr-IN"), //Marathi
(0x044f, "sa-IN"), //Sanskrit
(0x0450, "mn-Cyrl"), //Mongolian (Cyrillic)
(0x0451, "bo-CN"), //Tibetan
(0x0452, "cy-GB"), //Welsh
(0x0453, "km-KH"), //Khmer
(0x0454, "lo-LA"), //Lao
(0x0456, "gl-ES"), //Galician
(0x0457, "kok-IN"), //Konkani
(0x045a, "syr-SY"), //Syriac
(0x045b, "si-LK"), //Sinhala
(0x045d, "iu-Cans-CA"), //Inuktitut (Syllabics)
(0x045e, "am-ET"), //Amharic
(0x0461, "ne-NP"), //Nepali
(0x0462, "fy-NL"), //Frisian
(0x0463, "ps-AF"), //Pashto
(0x0464, "fil-PH"), //Filipino
(0x0465, "dv-MV"), //Divehi
(0x0468, "ha-Latn-NG"), //Hausa (Latin)
(0x046a, "yo-NG"), //Yoruba
(0x046b, "quz-BO"), //Quechua
(0x046c, "nso-ZA"), //Sesotho sa Leboa
(0x046d, "ba-RU"), //Bashkir
(0x046e, "lb-LU"), //Luxembourgish
(0x046f, "kl-GL"), //Greenlandic
(0x0470, "ig-NG"), //Igbo
(0x0478, "ii-CN"), //Yi
(0x047a, "arn-CL"), //Mapudungun
(0x047c, "moh-CA"), //Mohawk
(0x047e, "br-FR"), //Breton
(0x0480, "ug-CN"), //Uyghur
(0x0481, "mi-NZ"), //Maori
(0x0482, "oc-FR"), //Occitan
(0x0483, "co-FR"), //Corsican
(0x0484, "gsw-FR"), //Alsatian
(0x0485, "sah-RU"), //Yakut
(0x0486, "qut-GT"), //K'iche
(0x0487, "rw-RW"), //Kinyarwanda
(0x0488, "wo-SN"), //Wolof
(0x048c, "prs-AF"), //Dari
(0x0491, "gd-GB"), //Scottish Gaelic
(0x0801, "ar-IQ"), //Arabic
(0x0804, "zh-Hans"), //Chinese (Simplified)
(0x0807, "de-CH"), //German
(0x0809, "en-GB"), //English
(0x080a, "es-MX"), //Spanish
(0x080c, "fr-BE"), //French
(0x0810, "it-CH"), //Italian
(0x0813, "nl-BE"), //Dutch
(0x0814, "nn-NO"), //Norwegian (Nynorsk)
(0x0816, "pt-PT"), //Portuguese
(0x081a, "sr-Latn-CS"), //Serbian (Latin)
(0x081d, "sv-FI"), //Swedish
(0x082c, "az-Cyrl-AZ"), //Azeri (Cyrillic)
(0x082e, "dsb-DE"), //Lower Sorbian
(0x082e, "dsb"), //Lower Sorbian
(0x083b, "se-SE"), //Sami (Northern)
(0x083c, "ga-IE"), //Irish
(0x083e, "ms-BN"), //Malay
(0x0843, "uz-Cyrl-UZ"), //Uzbek (Cyrillic)
(0x0845, "bn-BD"), //Bengali
(0x0850, "mn-Mong-CN"), //Mongolian (Traditional Mongolian)
(0x085d, "iu-Latn-CA"), //Inuktitut (Latin)
(0x085f, "tzm-Latn-DZ"), //Tamazight (Latin)
(0x086b, "quz-EC"), //Quechua
(0x0c01, "ar-EG"), //Arabic
(0x0c04, "zh-Hant"), //Chinese (Traditional)
(0x0c07, "de-AT"), //German
(0x0c09, "en-AU"), //English
(0x0c0a, "es-ES"), //Spanish
(0x0c0c, "fr-CA"), //French
(0x0c1a, "sr-Cyrl-CS"), //Serbian (Cyrillic)
(0x0c3b, "se-FI"), //Sami (Northern)
(0x0c6b, "quz-PE"), //Quechua
(0x1001, "ar-LY"), //Arabic
(0x1004, "zh-SG"), //Chinese (Simplified)
(0x1007, "de-LU"), //German
(0x1009, "en-CA"), //English
(0x100a, "es-GT"), //Spanish
(0x100c, "fr-CH"), //French
(0x101a, "hr-BA"), //Croatian (Latin)
(0x103b, "smj-NO"), //Sami (Lule)
(0x1401, "ar-DZ"), //Arabic
(0x1404, "zh-MO"), //Chinese (Traditional)
(0x1407, "de-LI"), //German
(0x1409, "en-NZ"), //English
(0x140a, "es-CR"), //Spanish
(0x140c, "fr-LU"), //French
(0x141a, "bs-Latn-BA"), //Bosnian (Latin)
(0x141a, "bs"), //Bosnian
(0x143b, "smj-SE"), //Sami (Lule)
(0x143b, "smj"), //Sami (Lule)
(0x1801, "ar-MA"), //Arabic
(0x1809, "en-IE"), //English
(0x180a, "es-PA"), //Spanish
(0x180c, "fr-MC"), //French
(0x181a, "sr-Latn-BA"), //Serbian (Latin)
(0x183b, "sma-NO"), //Sami (Southern)
(0x1c01, "ar-TN"), //Arabic
(0x1c09, "en-ZA"), //English
(0x1c0a, "es-DO"), //Spanish
(0x1c1a, "sr-Cyrl-BA"), //Serbian (Cyrillic)
(0x1c3b, "sma-SE"), //Sami (Southern)
(0x1c3b, "sma"), //Sami (Southern)
(0x2001, "ar-OM"), //Arabic
(0x2009, "en-JM"), //English
(0x200a, "es-VE"), //Spanish
(0x201a, "bs-Cyrl-BA"), //Bosnian (Cyrillic)
(0x201a, "bs-Cyrl"), //Bosnian (Cyrillic)
(0x203b, "sms-FI"), //Sami (Skolt)
(0x203b, "sms"), //Sami (Skolt)
(0x2401, "ar-YE"), //Arabic
(0x2409, "en-029"), //English
(0x240a, "es-CO"), //Spanish
(0x241a, "sr-Latn-RS"), //Serbian (Latin)
(0x243b, "smn-FI"), //Sami (Inari)
(0x2801, "ar-SY"), //Arabic
(0x2809, "en-BZ"), //English
(0x280a, "es-PE"), //Spanish
(0x281a, "sr-Cyrl-RS"), //Serbian (Cyrillic)
(0x2c01, "ar-JO"), //Arabic
(0x2c09, "en-TT"), //English
(0x2c0a, "es-AR"), //Spanish
(0x2c1a, "sr-Latn-ME"), //Serbian (Latin)
(0x3001, "ar-LB"), //Arabic
(0x3009, "en-ZW"), //English
(0x300a, "es-EC"), //Spanish
(0x301a, "sr-Cyrl-ME"), //Serbian (Cyrillic)
(0x3401, "ar-KW"), //Arabic
(0x3409, "en-PH"), //English
(0x340a, "es-CL"), //Spanish
(0x3801, "ar-AE"), //Arabic
(0x380a, "es-UY"), //Spanish
(0x3c01, "ar-BH"), //Arabic
(0x3c0a, "es-PY"), //Spanish
(0x4001, "ar-QA"), //Arabic
(0x4009, "en-IN"), //English
(0x400a, "es-BO"), //Spanish
(0x4409, "en-MY"), //English
(0x440a, "es-SV"), //Spanish
(0x4809, "en-SG"), //English
(0x480a, "es-HN"), //Spanish
(0x4c0a, "es-NI"), //Spanish
(0x500a, "es-PR"), //Spanish
(0x540a, "es-US"), //Spanish
];
#[cfg(test)]
mod tests {
use crate::MetadataProvider;
use super::*;
use read_fonts::FontRef;
#[test]
fn localized() {
let font = FontRef::new(font_test_data::NAMES_ONLY).unwrap();
let mut subfamily_names = font
.localized_strings(StringId::SUBFAMILY_NAME)
.map(|s| (s.language().unwrap().to_string(), s.to_string()))
.collect::<Vec<_>>();
subfamily_names.sort_by(|a, b| a.0.cmp(&b.0));
let expected = [
(String::from("ar-SA"), String::from("عادي")),
(String::from("el-GR"), String::from("Κανονικά")),
(String::from("en"), String::from("Regular")),
(String::from("eu-ES"), String::from("Arrunta")),
(String::from("pl-PL"), String::from("Normalny")),
(String::from("zh-Hans"), String::from("正常")),
];
assert_eq!(subfamily_names.as_slice(), expected);
}
#[test]
fn find_by_language() {
let font = FontRef::new(font_test_data::NAMES_ONLY).unwrap();
assert_eq!(
font.localized_strings(StringId::SUBFAMILY_NAME)
.find(|s| s.language() == Some("pl-PL"))
.unwrap()
.to_string(),
"Normalny"
);
}
#[test]
fn english_or_first() {
let font = FontRef::new(font_test_data::NAMES_ONLY).unwrap();
assert_eq!(
font.localized_strings(StringId::SUBFAMILY_NAME)
.english_or_first()
.unwrap()
.to_string(),
"Regular"
);
}
}