#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum CharacterCollection {
AdobeJapan1,
AdobeGB1,
AdobeCNS1,
AdobeKorea1,
}
impl CharacterCollection {
#[inline]
pub fn cid_to_unicode(self, cid: u16) -> Option<u32> {
match self {
CharacterCollection::AdobeJapan1 => super::cid_mappings::lookup_adobe_japan1(cid),
CharacterCollection::AdobeGB1 => super::cid_mappings::lookup_adobe_gb1(cid),
CharacterCollection::AdobeCNS1 => super::cid_mappings::lookup_adobe_cns1(cid),
CharacterCollection::AdobeKorea1 => super::cid_mappings::lookup_adobe_korea1(cid),
}
}
}
const KNOWN_CMAP_SUFFIXES: &[&str] = &[
"Identity-H",
"Identity-V",
"UniJIS-UCS2-H",
"UniJIS-UCS2-V",
"UniJIS-UCS2-HW-H",
"UniJIS-UCS2-HW-V",
"UniJIS-UTF16-H",
"UniJIS-UTF16-V",
"UniJIS-UTF8-H",
"UniJIS-UTF8-V",
"UniJIS-X0213-UTF32-H",
"UniJIS-X0213-UTF32-V",
"UniJIS-X02132004-UTF32-H",
"UniJIS-X02132004-UTF32-V",
"UniJISPro-UCS2-HW-V",
"UniJISPro-UCS2-V",
"UniJISX0213-UTF32-H",
"UniJISX0213-UTF32-V",
"UniJISX02132004-UTF32-H",
"UniJISX02132004-UTF32-V",
"90ms-RKSJ-H",
"90ms-RKSJ-V",
"90msp-RKSJ-H",
"90msp-RKSJ-V",
"90pv-RKSJ-H",
"90pv-RKSJ-V",
"78ms-RKSJ-H",
"78ms-RKSJ-V",
"83pv-RKSJ-H",
"Add-RKSJ-H",
"Add-RKSJ-V",
"EUC-H",
"EUC-V",
"Ext-RKSJ-H",
"Ext-RKSJ-V",
"H",
"V",
"WP-Symbol",
"Hojo-EUC-H",
"Hojo-EUC-V",
"Hojo-H",
"Hojo-V",
"Hankaku",
"Hiragana",
"Katakana",
"Roman",
"UniGB-UCS2-H",
"UniGB-UCS2-V",
"UniGB-UTF16-H",
"UniGB-UTF16-V",
"UniGB-UTF8-H",
"UniGB-UTF8-V",
"GB-EUC-H",
"GB-EUC-V",
"GBK-EUC-H",
"GBK-EUC-V",
"GBK2K-H",
"GBK2K-V",
"GBKp-EUC-H",
"GBKp-EUC-V",
"GBpc-EUC-H",
"GBpc-EUC-V",
"GBT-EUC-H",
"GBT-EUC-V",
"GBT-H",
"GBT-V",
"GBTpc-EUC-H",
"UniCNS-UCS2-H",
"UniCNS-UCS2-V",
"UniCNS-UTF16-H",
"UniCNS-UTF16-V",
"UniCNS-UTF8-H",
"UniCNS-UTF8-V",
"B5pc-H",
"B5pc-V",
"ETen-B5-H",
"ETen-B5-V",
"ETenms-B5-H",
"ETenms-B5-V",
"CNS-EUC-H",
"CNS-EUC-V",
"HKscs-B5-H",
"HKscs-B5-V",
"UniKS-UCS2-H",
"UniKS-UCS2-V",
"UniKS-UTF16-H",
"UniKS-UTF16-V",
"UniKS-UTF8-H",
"UniKS-UTF8-V",
"KSC-EUC-H",
"KSC-EUC-V",
"KSCms-UHC-H",
"KSCms-UHC-V",
"KSCms-UHC-HW-H",
"KSCms-UHC-HW-V",
"KSCpc-EUC-H",
];
fn strip_cmap_suffix(name: &str) -> &str {
let mut best: Option<&str> = None;
let mut best_len: usize = 0;
for suffix in KNOWN_CMAP_SUFFIXES {
let trailer = format!("-{}", suffix);
if let Some(stripped) = name.strip_suffix(&trailer) {
if trailer.len() > best_len {
best_len = trailer.len();
best = Some(stripped);
}
}
}
best.unwrap_or(name)
}
fn collection_for_bare_name(name: &str) -> Option<CharacterCollection> {
use CharacterCollection::*;
if matches!(
name,
"Ryumin-Light"
| "Ryumin-Medium"
| "Ryumin-Regular"
| "Ryumin-Heavy"
| "Ryumin-Bold"
| "Ryumin-Ultra"
| "GothicBBB-Medium"
| "GothicMB101-Bold"
| "FutoGoB101-Bold"
| "FutoMinA101-Bold"
| "Jun101-Light"
| "MidashiGo-MB31"
| "MidashiMin-MA31"
| "HeiseiMin-W3"
| "HeiseiMin-W5"
| "HeiseiMin-W7"
| "HeiseiMin-W9"
| "HeiseiKakuGo-W3"
| "HeiseiKakuGo-W5"
| "HeiseiKakuGo-W7"
| "HeiseiKakuGo-W9"
| "HeiseiMaruGo-W4"
| "KozMinPro-Regular"
| "KozMinPro-Light"
| "KozMinPro-Medium"
| "KozMinPro-Bold"
| "KozMinPro-Heavy"
| "KozMinProVI-Regular"
| "KozMinProVI-Light"
| "KozMinProVI-Medium"
| "KozMinProVI-Bold"
| "KozMinProVI-Heavy"
| "KozGoPro-Regular"
| "KozGoPro-Light"
| "KozGoPro-Medium"
| "KozGoPro-Bold"
| "KozGoPro-Heavy"
| "KozGoProVI-Regular"
| "KozGoProVI-Light"
| "KozGoProVI-Medium"
| "KozGoProVI-Bold"
| "KozGoProVI-Heavy"
| "Kozuka-Mincho-Pro-VI-R"
| "Kozuka-Gothic-Pro-VI-M"
) {
return Some(AdobeJapan1);
}
if matches!(
name,
"STSong-Light"
| "STSongStd-Light"
| "STSong-Regular"
| "STSongStd-Regular"
| "STHeiti-Regular"
| "STHeiti-Light"
| "STHeitiSC-Regular"
| "STHeitiSC-Light"
| "STKaiti-Regular"
| "STKaitiStd-Regular"
| "STFangsong-Light"
| "STFangsong-Regular"
| "SimSun"
| "SimHei"
| "SimSun-ExtB"
| "AdobeSongStd-Light"
| "AdobeHeitiStd-Regular"
| "AdobeKaitiStd-Regular"
| "AdobeFangsongStd-Regular"
) {
return Some(AdobeGB1);
}
if matches!(
name,
"MHei-Medium"
| "MSung-Light"
| "MSung-Medium"
| "MSungStd-Light"
| "MSungStd-Medium"
| "MSungStd-Light-Acro"
| "MingLiU"
| "PMingLiU"
| "MingLiU-ExtB"
| "PMingLiU-ExtB"
| "DFKaiShu-SB-Estd-BF"
| "DFKaiSho-W5"
| "HeiseiKakuGoStd-W5"
| "AdobeMingStd-Light"
| "AdobeFanHeitiStd-Bold"
| "AdobeSongStd-Bold"
) {
return Some(AdobeCNS1);
}
if matches!(
name,
"HYSMyeongJo-Medium"
| "HYSMyeongJoStd-Medium"
| "HYGoThic-Medium"
| "HYGothic-Medium"
| "HYGothic-Bold"
| "HYGothicStd-Medium"
| "HYRGoThic-Medium"
| "HYHeadLine-Medium"
| "Adobe-MyungjoStd-Medium"
| "AdobeMyungjoStd-Medium"
| "AdobeGothicStd-Bold"
| "Batang"
| "BatangChe"
| "Dotum"
| "DotumChe"
| "Gulim"
| "GulimChe"
| "Gungsuh"
| "GungsuhChe"
) {
return Some(AdobeKorea1);
}
None
}
pub fn is_predefined(base_font: &str) -> Option<CharacterCollection> {
let after_prefix = match base_font.find('+') {
Some(plus_idx) if plus_idx == 6 => {
let prefix = &base_font[..plus_idx];
if prefix.chars().all(|c| c.is_ascii_uppercase()) {
&base_font[plus_idx + 1..]
} else {
base_font
}
},
_ => base_font,
};
let bare = strip_cmap_suffix(after_prefix);
collection_for_bare_name(bare)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bare_japanese_names_resolve_to_japan1() {
assert_eq!(is_predefined("Ryumin-Light"), Some(CharacterCollection::AdobeJapan1));
assert_eq!(is_predefined("GothicBBB-Medium"), Some(CharacterCollection::AdobeJapan1));
assert_eq!(is_predefined("HeiseiMin-W3"), Some(CharacterCollection::AdobeJapan1));
assert_eq!(is_predefined("HeiseiKakuGo-W5"), Some(CharacterCollection::AdobeJapan1));
assert_eq!(is_predefined("KozMinPro-Regular"), Some(CharacterCollection::AdobeJapan1));
assert_eq!(is_predefined("Kozuka-Mincho-Pro-VI-R"), Some(CharacterCollection::AdobeJapan1));
}
#[test]
fn suffixed_japanese_names_resolve_to_japan1() {
assert_eq!(
is_predefined("Ryumin-Light-Identity-V"),
Some(CharacterCollection::AdobeJapan1)
);
assert_eq!(
is_predefined("Ryumin-Light-Identity-H"),
Some(CharacterCollection::AdobeJapan1)
);
assert_eq!(
is_predefined("Ryumin-Light-90ms-RKSJ-H"),
Some(CharacterCollection::AdobeJapan1)
);
assert_eq!(
is_predefined("GothicBBB-Medium-Identity-H"),
Some(CharacterCollection::AdobeJapan1)
);
assert_eq!(
is_predefined("HeiseiKakuGo-W5-UniJIS-UCS2-H"),
Some(CharacterCollection::AdobeJapan1)
);
}
#[test]
fn bare_chinese_simplified_names_resolve_to_gb1() {
assert_eq!(is_predefined("STSong-Light"), Some(CharacterCollection::AdobeGB1));
assert_eq!(is_predefined("STSongStd-Light"), Some(CharacterCollection::AdobeGB1));
assert_eq!(is_predefined("STHeiti-Light"), Some(CharacterCollection::AdobeGB1));
assert_eq!(is_predefined("SimSun"), Some(CharacterCollection::AdobeGB1));
assert_eq!(is_predefined("SimHei"), Some(CharacterCollection::AdobeGB1));
}
#[test]
fn suffixed_chinese_simplified_names_resolve_to_gb1() {
assert_eq!(is_predefined("STSong-Light-GBK-EUC-H"), Some(CharacterCollection::AdobeGB1));
assert_eq!(is_predefined("STSong-Light-UniGB-UCS2-H"), Some(CharacterCollection::AdobeGB1));
assert_eq!(is_predefined("STHeiti-Light-Identity-V"), Some(CharacterCollection::AdobeGB1));
}
#[test]
fn bare_chinese_traditional_names_resolve_to_cns1() {
assert_eq!(is_predefined("MHei-Medium"), Some(CharacterCollection::AdobeCNS1));
assert_eq!(is_predefined("MSung-Light"), Some(CharacterCollection::AdobeCNS1));
assert_eq!(is_predefined("MSungStd-Light"), Some(CharacterCollection::AdobeCNS1));
assert_eq!(is_predefined("MingLiU"), Some(CharacterCollection::AdobeCNS1));
assert_eq!(is_predefined("PMingLiU"), Some(CharacterCollection::AdobeCNS1));
}
#[test]
fn suffixed_chinese_traditional_names_resolve_to_cns1() {
assert_eq!(is_predefined("MHei-Medium-B5pc-H"), Some(CharacterCollection::AdobeCNS1));
assert_eq!(is_predefined("MSung-Light-Identity-H"), Some(CharacterCollection::AdobeCNS1));
assert_eq!(is_predefined("MingLiU-ETen-B5-V"), Some(CharacterCollection::AdobeCNS1));
}
#[test]
fn bare_korean_names_resolve_to_korea1() {
assert_eq!(is_predefined("HYSMyeongJo-Medium"), Some(CharacterCollection::AdobeKorea1));
assert_eq!(is_predefined("HYGoThic-Medium"), Some(CharacterCollection::AdobeKorea1));
assert_eq!(
is_predefined("Adobe-MyungjoStd-Medium"),
Some(CharacterCollection::AdobeKorea1)
);
assert_eq!(is_predefined("Batang"), Some(CharacterCollection::AdobeKorea1));
}
#[test]
fn suffixed_korean_names_resolve_to_korea1() {
assert_eq!(
is_predefined("HYSMyeongJo-Medium-KSC-EUC-H"),
Some(CharacterCollection::AdobeKorea1)
);
assert_eq!(
is_predefined("HYGoThic-Medium-Identity-V"),
Some(CharacterCollection::AdobeKorea1)
);
}
#[test]
fn subset_prefix_is_stripped() {
assert_eq!(
is_predefined("ABCDEF+Ryumin-Light-Identity-V"),
Some(CharacterCollection::AdobeJapan1)
);
assert_eq!(is_predefined("XEAACC+STSong-Light"), Some(CharacterCollection::AdobeGB1));
}
#[test]
fn unrelated_fonts_return_none() {
assert_eq!(is_predefined("ArialMT"), None);
assert_eq!(is_predefined("Helvetica"), None);
assert_eq!(is_predefined("Times-Roman"), None);
assert_eq!(is_predefined("DejaVuSans"), None);
assert_eq!(is_predefined("AGaramondPro-Regular"), None);
}
#[test]
fn cmap_suffix_strip_does_not_swallow_base_name() {
assert_eq!(strip_cmap_suffix("Ryumin-Light"), "Ryumin-Light");
assert_eq!(strip_cmap_suffix("STSong-Light"), "STSong-Light");
}
#[test]
fn character_collection_cid_to_unicode_routes_to_correct_table() {
assert_eq!(CharacterCollection::AdobeJapan1.cid_to_unicode(34), Some(0x0041));
assert_eq!(CharacterCollection::AdobeJapan1.cid_to_unicode(843), Some(0x3042));
assert_eq!(CharacterCollection::AdobeGB1.cid_to_unicode(34), Some(0x0041));
assert_eq!(CharacterCollection::AdobeCNS1.cid_to_unicode(34), Some(0x0041));
assert_eq!(CharacterCollection::AdobeKorea1.cid_to_unicode(1086), Some(0xAC00));
}
}