cryosnap-core 0.2.11

Core rendering library for cryosnap.
Documentation
use super::constants::{
    AUTO_FALLBACK_CJK_HK, AUTO_FALLBACK_CJK_JP, AUTO_FALLBACK_CJK_KR, AUTO_FALLBACK_CJK_SC,
    AUTO_FALLBACK_CJK_TC, NOTO_CJK_HK_URLS, NOTO_CJK_JP_URLS, NOTO_CJK_KR_URLS, NOTO_CJK_SC_URLS,
    NOTO_CJK_TC_URLS,
};
use super::models::FontFallbackNeeds;
use crate::{CjkRegion, Config};
use std::collections::HashSet;
use std::env;
use unicode_script::Script;

pub(crate) fn parse_cjk_region_from_locale(value: &str) -> Option<CjkRegion> {
    let raw = value.trim();
    if raw.is_empty() {
        return None;
    }
    let mut base = raw.to_ascii_lowercase();
    if let Some(pos) = base.find(['.', '@']) {
        base.truncate(pos);
    }
    let normalized = base.replace('-', "_");
    let parts = normalized
        .split('_')
        .filter(|part| !part.is_empty())
        .collect::<Vec<_>>();
    if parts.is_empty() {
        return None;
    }
    match parts[0] {
        "ja" => return Some(CjkRegion::Jp),
        "ko" => return Some(CjkRegion::Kr),
        "zh" => {
            if parts.iter().any(|part| matches!(*part, "hk" | "mo")) {
                return Some(CjkRegion::Hk);
            }
            if parts.contains(&"tw") {
                return Some(CjkRegion::Tc);
            }
            if parts.contains(&"hant") {
                return Some(CjkRegion::Tc);
            }
            if parts.iter().any(|part| matches!(*part, "cn" | "sg")) {
                return Some(CjkRegion::Sc);
            }
            if parts.contains(&"hans") {
                return Some(CjkRegion::Sc);
            }
        }
        _ => {}
    }
    None
}

pub(crate) fn locale_cjk_region() -> Option<CjkRegion> {
    for key in ["LC_ALL", "LC_CTYPE", "LANG"] {
        if let Ok(value) = env::var(key) {
            if let Some(region) = parse_cjk_region_from_locale(&value) {
                return Some(region);
            }
        }
    }
    None
}

pub(crate) fn push_cjk_region(
    out: &mut Vec<CjkRegion>,
    seen: &mut HashSet<CjkRegion>,
    region: CjkRegion,
) {
    if seen.insert(region) {
        out.push(region);
    }
}

pub(crate) fn collect_cjk_regions(config: &Config, needs: &FontFallbackNeeds) -> Vec<CjkRegion> {
    if !needs.needs_cjk {
        return Vec::new();
    }
    let mut out = Vec::new();
    let mut seen = HashSet::new();

    if needs.scripts.contains(&Script::Hiragana) || needs.scripts.contains(&Script::Katakana) {
        push_cjk_region(&mut out, &mut seen, CjkRegion::Jp);
    }
    if needs.scripts.contains(&Script::Hangul) {
        push_cjk_region(&mut out, &mut seen, CjkRegion::Kr);
    }
    if needs.scripts.contains(&Script::Bopomofo) {
        push_cjk_region(&mut out, &mut seen, CjkRegion::Tc);
    }

    if needs.scripts.contains(&Script::Han) || out.is_empty() {
        let region = match config.font.cjk_region {
            CjkRegion::Auto => locale_cjk_region().unwrap_or(CjkRegion::Sc),
            other => other,
        };
        push_cjk_region(&mut out, &mut seen, region);
    }

    out
}

pub(crate) fn cjk_region_families(region: CjkRegion) -> &'static [&'static str] {
    match region {
        CjkRegion::Sc => AUTO_FALLBACK_CJK_SC,
        CjkRegion::Tc => AUTO_FALLBACK_CJK_TC,
        CjkRegion::Hk => AUTO_FALLBACK_CJK_HK,
        CjkRegion::Jp => AUTO_FALLBACK_CJK_JP,
        CjkRegion::Kr => AUTO_FALLBACK_CJK_KR,
        CjkRegion::Auto => AUTO_FALLBACK_CJK_SC,
    }
}

pub(crate) fn cjk_region_urls(region: CjkRegion) -> &'static [&'static str] {
    match region {
        CjkRegion::Sc => NOTO_CJK_SC_URLS,
        CjkRegion::Tc => NOTO_CJK_TC_URLS,
        CjkRegion::Hk => NOTO_CJK_HK_URLS,
        CjkRegion::Jp => NOTO_CJK_JP_URLS,
        CjkRegion::Kr => NOTO_CJK_KR_URLS,
        CjkRegion::Auto => NOTO_CJK_SC_URLS,
    }
}

pub(crate) fn cjk_region_filename(region: CjkRegion) -> &'static str {
    match region {
        CjkRegion::Sc => "NotoSansCJKsc-Regular.otf",
        CjkRegion::Tc => "NotoSansCJKtc-Regular.otf",
        CjkRegion::Hk => "NotoSansCJKhk-Regular.otf",
        CjkRegion::Jp => "NotoSansCJKjp-Regular.otf",
        CjkRegion::Kr => "NotoSansCJKkr-Regular.otf",
        CjkRegion::Auto => "NotoSansCJKsc-Regular.otf",
    }
}