use crate::FontFlags;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Charset {
#[default]
Ansi,
Default,
Symbol,
ShiftJis,
Hangul,
ChineseSimplified,
ChineseTraditional,
Johab,
Greek,
Turkish,
Vietnamese,
Hebrew,
Arabic,
Baltic,
Cyrillic,
Thai,
EasternEuropean,
Oem,
}
impl Charset {
#[must_use]
pub(crate) fn is_cjk(self) -> bool {
matches!(
self,
Self::ChineseSimplified | Self::ChineseTraditional | Self::Hangul | Self::ShiftJis
)
}
#[must_use]
pub(crate) fn from_code_page(cp: CodePage) -> Self {
match cp {
CodePage::ShiftJis => Self::ShiftJis,
CodePage::ChineseSimplified => Self::ChineseSimplified,
CodePage::Hangul => Self::Hangul,
CodePage::ChineseTraditional => Self::ChineseTraditional,
CodePage::DefAnsi | CodePage::Utf16Le => Self::Ansi,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum CodePage {
#[default]
DefAnsi,
ShiftJis,
ChineseSimplified,
Hangul,
ChineseTraditional,
Utf16Le,
}
impl CodePage {
#[must_use]
pub fn for_cid_set(set: pdfrum_cmap::CidSet) -> Self {
use pdfrum_cmap::CidSet;
match set {
CidSet::Unknown => Self::DefAnsi,
CidSet::Gb1 => Self::ChineseSimplified,
CidSet::Cns1 => Self::ChineseTraditional,
CidSet::Japan1 => Self::ShiftJis,
CidSet::Korea1 => Self::Hangul,
CidSet::Unicode => Self::Utf16Le,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct PitchFamily(pub u32);
impl PitchFamily {
pub const FIXED: u32 = 1;
pub const ROMAN: u32 = 16;
pub const SCRIPT: u32 = 64;
#[must_use]
pub const fn has(self, bit: u32) -> bool {
self.0 & bit != 0
}
#[must_use]
pub fn from_flags(flags: FontFlags) -> Self {
let mut p = 0;
if flags.contains(FontFlags::SERIF) {
p |= Self::ROMAN;
}
if flags.contains(FontFlags::SCRIPT) {
p |= Self::SCRIPT;
}
if flags.contains(FontFlags::FIXED_PITCH) {
p |= Self::FIXED;
}
Self(p)
}
#[must_use]
pub fn from_standard_font(f: super::StandardFont) -> Self {
let i = f.index();
if i < 4 {
Self(Self::FIXED)
} else if i >= 8 {
Self(Self::ROMAN)
} else {
Self(0)
}
}
}
#[allow(clippy::match_same_arms)]
#[must_use]
pub fn charset_from_unicode(u: u32) -> Charset {
match u {
0..=0x7E => Charset::Ansi,
0x4E00..=0x9FA5 | 0xE7C7..=0xE7F3 | 0x3000..=0x303F | 0x2000..=0x206F => {
Charset::ChineseSimplified
}
0x3040..=0x309F | 0x30A0..=0x30FF | 0x31F0..=0x31FF | 0xFF00..=0xFFEF => Charset::ShiftJis,
0xAC00..=0xD7AF | 0x1100..=0x11FF | 0x3130..=0x318F => Charset::Hangul,
0x0E00..=0x0E7F => Charset::Thai,
0x0370..=0x03FF | 0x1F00..=0x1FFF => Charset::Greek,
0x0600..=0x06FF | 0xFB50..=0xFEFC => Charset::Arabic,
0x0590..=0x05FF => Charset::Hebrew,
0x0400..=0x04FF => Charset::Cyrillic,
0x0100..=0x024F => Charset::EasternEuropean,
0x1E00..=0x1EFF => Charset::Vietnamese,
_ => Charset::Ansi,
}
}
#[cfg(all(feature = "system-fonts", not(target_arch = "wasm32")))]
#[must_use]
pub fn charset_for_code_page_bit(bit: u32) -> Option<Charset> {
Some(match bit {
1 => Charset::EasternEuropean,
2 => Charset::Cyrillic,
3 => Charset::Greek,
4 => Charset::Turkish,
5 => Charset::Hebrew,
6 => Charset::Arabic,
7 => Charset::Baltic,
8 => Charset::Vietnamese,
16 => Charset::Thai,
17 => Charset::ShiftJis,
18 => Charset::ChineseSimplified,
19 => Charset::Hangul,
20 => Charset::ChineseTraditional,
21 => Charset::Johab,
30 => Charset::Oem,
31 => Charset::Symbol,
_ => return None,
})
}
#[must_use]
#[cfg(test)]
pub fn default_face_name(charset: Charset) -> &'static str {
match charset {
Charset::Ansi => "Helvetica",
Charset::ChineseSimplified => "SimSun",
Charset::ChineseTraditional => "MingLiU",
Charset::ShiftJis => "MS Gothic",
Charset::Hangul => "Batang",
Charset::Cyrillic | Charset::EasternEuropean | Charset::Arabic => "Arial",
_ => "Arial Unicode MS",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_four_charsets_count_as_cjk() {
for c in [
Charset::ChineseSimplified,
Charset::ChineseTraditional,
Charset::Hangul,
Charset::ShiftJis,
] {
assert!(c.is_cjk(), "{c:?}");
}
assert!(!Charset::Johab.is_cjk());
for c in [
Charset::Ansi,
Charset::Symbol,
Charset::Thai,
Charset::Default,
] {
assert!(!c.is_cjk(), "{c:?}");
}
}
#[test]
fn each_cid_collection_maps_to_its_code_page() {
use pdfrum_cmap::CidSet;
assert_eq!(CodePage::for_cid_set(CidSet::Unknown), CodePage::DefAnsi);
assert_eq!(
CodePage::for_cid_set(CidSet::Gb1),
CodePage::ChineseSimplified
);
assert_eq!(
CodePage::for_cid_set(CidSet::Cns1),
CodePage::ChineseTraditional
);
assert_eq!(CodePage::for_cid_set(CidSet::Japan1), CodePage::ShiftJis);
assert_eq!(CodePage::for_cid_set(CidSet::Korea1), CodePage::Hangul);
assert_eq!(CodePage::for_cid_set(CidSet::Unicode), CodePage::Utf16Le);
}
#[test]
fn the_charset_ladder_is_ordered_and_ascii_wins_first() {
assert_eq!(charset_from_unicode(0x41), Charset::Ansi);
assert_eq!(charset_from_unicode(0x7E), Charset::Ansi);
assert_eq!(charset_from_unicode(0x7F), Charset::Ansi);
assert_eq!(charset_from_unicode(0x4E00), Charset::ChineseSimplified);
assert_eq!(charset_from_unicode(0x2014), Charset::ChineseSimplified);
assert_eq!(charset_from_unicode(0x3042), Charset::ShiftJis);
assert_eq!(charset_from_unicode(0xAC00), Charset::Hangul);
assert_eq!(charset_from_unicode(0x0E01), Charset::Thai);
assert_eq!(charset_from_unicode(0x03B1), Charset::Greek);
assert_eq!(charset_from_unicode(0x0627), Charset::Arabic);
assert_eq!(charset_from_unicode(0x05D0), Charset::Hebrew);
assert_eq!(charset_from_unicode(0x0410), Charset::Cyrillic);
assert_eq!(charset_from_unicode(0x0100), Charset::EasternEuropean);
assert_eq!(charset_from_unicode(0x1E00), Charset::Vietnamese);
assert_eq!(charset_from_unicode(0x10000), Charset::Ansi);
}
#[test]
fn pitch_families_derive_from_both_sources() {
use super::super::StandardFont;
assert_eq!(
PitchFamily::from_standard_font(StandardFont::Courier).0,
PitchFamily::FIXED
);
assert_eq!(
PitchFamily::from_standard_font(StandardFont::Helvetica).0,
0
);
assert_eq!(
PitchFamily::from_standard_font(StandardFont::Times).0,
PitchFamily::ROMAN
);
assert_eq!(
PitchFamily::from_standard_font(StandardFont::Symbol).0,
PitchFamily::ROMAN
);
let flags = FontFlags::SERIF | FontFlags::FIXED_PITCH;
let p = PitchFamily::from_flags(flags);
assert!(p.has(PitchFamily::ROMAN));
assert!(p.has(PitchFamily::FIXED));
assert!(!p.has(PitchFamily::SCRIPT));
}
#[cfg(all(feature = "system-fonts", not(target_arch = "wasm32")))]
#[test]
fn code_page_bits_map_to_charsets() {
assert_eq!(charset_for_code_page_bit(17), Some(Charset::ShiftJis));
assert_eq!(charset_for_code_page_bit(31), Some(Charset::Symbol));
assert_eq!(charset_for_code_page_bit(0), None);
assert_eq!(charset_for_code_page_bit(29), None);
}
#[test]
fn default_face_names_match_the_linux_table() {
assert_eq!(default_face_name(Charset::Ansi), "Helvetica");
assert_eq!(default_face_name(Charset::ChineseSimplified), "SimSun");
assert_eq!(default_face_name(Charset::ShiftJis), "MS Gothic");
assert_eq!(default_face_name(Charset::Symbol), "Arial Unicode MS");
}
}