1use crate::FontFlags;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
11pub enum Charset {
12 #[default]
15 Ansi,
16 Default,
19 Symbol,
21 ShiftJis,
23 Hangul,
25 ChineseSimplified,
27 ChineseTraditional,
29 Johab,
31 Greek,
33 Turkish,
35 Vietnamese,
37 Hebrew,
39 Arabic,
41 Baltic,
43 Cyrillic,
45 Thai,
47 EasternEuropean,
49 Oem,
51}
52
53impl Charset {
54 #[must_use]
60 pub(crate) fn is_cjk(self) -> bool {
61 matches!(
62 self,
63 Self::ChineseSimplified | Self::ChineseTraditional | Self::Hangul | Self::ShiftJis
64 )
65 }
66
67 #[must_use]
69 pub(crate) fn from_code_page(cp: CodePage) -> Self {
70 match cp {
71 CodePage::ShiftJis => Self::ShiftJis,
72 CodePage::ChineseSimplified => Self::ChineseSimplified,
73 CodePage::Hangul => Self::Hangul,
74 CodePage::ChineseTraditional => Self::ChineseTraditional,
75 CodePage::DefAnsi | CodePage::Utf16Le => Self::Ansi,
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
82pub enum CodePage {
83 #[default]
85 DefAnsi,
86 ShiftJis,
88 ChineseSimplified,
90 Hangul,
92 ChineseTraditional,
94 Utf16Le,
96}
97
98impl CodePage {
99 #[must_use]
101 pub fn for_cid_set(set: pdfrum_cmap::CidSet) -> Self {
102 use pdfrum_cmap::CidSet;
103 match set {
104 CidSet::Unknown => Self::DefAnsi,
105 CidSet::Gb1 => Self::ChineseSimplified,
106 CidSet::Cns1 => Self::ChineseTraditional,
107 CidSet::Japan1 => Self::ShiftJis,
108 CidSet::Korea1 => Self::Hangul,
109 CidSet::Unicode => Self::Utf16Le,
110 }
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
119pub struct PitchFamily(pub u32);
120
121impl PitchFamily {
122 pub const FIXED: u32 = 1;
124 pub const ROMAN: u32 = 16;
126 pub const SCRIPT: u32 = 64;
128
129 #[must_use]
131 pub const fn has(self, bit: u32) -> bool {
132 self.0 & bit != 0
133 }
134
135 #[must_use]
137 pub fn from_flags(flags: FontFlags) -> Self {
138 let mut p = 0;
139 if flags.contains(FontFlags::SERIF) {
140 p |= Self::ROMAN;
141 }
142 if flags.contains(FontFlags::SCRIPT) {
143 p |= Self::SCRIPT;
144 }
145 if flags.contains(FontFlags::FIXED_PITCH) {
146 p |= Self::FIXED;
147 }
148 Self(p)
149 }
150
151 #[must_use]
156 pub fn from_standard_font(f: super::StandardFont) -> Self {
157 let i = f.index();
158 if i < 4 {
159 Self(Self::FIXED)
160 } else if i >= 8 {
161 Self(Self::ROMAN)
162 } else {
163 Self(0)
164 }
165 }
166}
167
168#[allow(clippy::match_same_arms)]
177#[must_use]
178pub fn charset_from_unicode(u: u32) -> Charset {
179 match u {
180 0..=0x7E => Charset::Ansi,
182 0x4E00..=0x9FA5 | 0xE7C7..=0xE7F3 | 0x3000..=0x303F | 0x2000..=0x206F => {
183 Charset::ChineseSimplified
184 }
185 0x3040..=0x309F | 0x30A0..=0x30FF | 0x31F0..=0x31FF | 0xFF00..=0xFFEF => Charset::ShiftJis,
186 0xAC00..=0xD7AF | 0x1100..=0x11FF | 0x3130..=0x318F => Charset::Hangul,
187 0x0E00..=0x0E7F => Charset::Thai,
188 0x0370..=0x03FF | 0x1F00..=0x1FFF => Charset::Greek,
189 0x0600..=0x06FF | 0xFB50..=0xFEFC => Charset::Arabic,
190 0x0590..=0x05FF => Charset::Hebrew,
191 0x0400..=0x04FF => Charset::Cyrillic,
192 0x0100..=0x024F => Charset::EasternEuropean,
193 0x1E00..=0x1EFF => Charset::Vietnamese,
194 _ => Charset::Ansi,
195 }
196}
197
198#[cfg(all(feature = "system-fonts", not(target_arch = "wasm32")))]
199#[must_use]
202pub fn charset_for_code_page_bit(bit: u32) -> Option<Charset> {
203 Some(match bit {
204 1 => Charset::EasternEuropean,
205 2 => Charset::Cyrillic,
206 3 => Charset::Greek,
207 4 => Charset::Turkish,
208 5 => Charset::Hebrew,
209 6 => Charset::Arabic,
210 7 => Charset::Baltic,
211 8 => Charset::Vietnamese,
212 16 => Charset::Thai,
213 17 => Charset::ShiftJis,
214 18 => Charset::ChineseSimplified,
215 19 => Charset::Hangul,
216 20 => Charset::ChineseTraditional,
217 21 => Charset::Johab,
218 30 => Charset::Oem,
219 31 => Charset::Symbol,
220 _ => return None,
221 })
222}
223
224#[must_use]
227#[cfg(test)]
228pub fn default_face_name(charset: Charset) -> &'static str {
229 match charset {
230 Charset::Ansi => "Helvetica",
231 Charset::ChineseSimplified => "SimSun",
232 Charset::ChineseTraditional => "MingLiU",
233 Charset::ShiftJis => "MS Gothic",
234 Charset::Hangul => "Batang",
235 Charset::Cyrillic | Charset::EasternEuropean | Charset::Arabic => "Arial",
236 _ => "Arial Unicode MS",
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn only_four_charsets_count_as_cjk() {
246 for c in [
247 Charset::ChineseSimplified,
248 Charset::ChineseTraditional,
249 Charset::Hangul,
250 Charset::ShiftJis,
251 ] {
252 assert!(c.is_cjk(), "{c:?}");
253 }
254 assert!(!Charset::Johab.is_cjk());
256 for c in [
257 Charset::Ansi,
258 Charset::Symbol,
259 Charset::Thai,
260 Charset::Default,
261 ] {
262 assert!(!c.is_cjk(), "{c:?}");
263 }
264 }
265
266 #[test]
267 fn each_cid_collection_maps_to_its_code_page() {
268 use pdfrum_cmap::CidSet;
269 assert_eq!(CodePage::for_cid_set(CidSet::Unknown), CodePage::DefAnsi);
270 assert_eq!(
271 CodePage::for_cid_set(CidSet::Gb1),
272 CodePage::ChineseSimplified
273 );
274 assert_eq!(
275 CodePage::for_cid_set(CidSet::Cns1),
276 CodePage::ChineseTraditional
277 );
278 assert_eq!(CodePage::for_cid_set(CidSet::Japan1), CodePage::ShiftJis);
279 assert_eq!(CodePage::for_cid_set(CidSet::Korea1), CodePage::Hangul);
280 assert_eq!(CodePage::for_cid_set(CidSet::Unicode), CodePage::Utf16Le);
281 }
282
283 #[test]
284 fn the_charset_ladder_is_ordered_and_ascii_wins_first() {
285 assert_eq!(charset_from_unicode(0x41), Charset::Ansi);
286 assert_eq!(charset_from_unicode(0x7E), Charset::Ansi);
287 assert_eq!(charset_from_unicode(0x7F), Charset::Ansi);
289 assert_eq!(charset_from_unicode(0x4E00), Charset::ChineseSimplified);
290 assert_eq!(charset_from_unicode(0x2014), Charset::ChineseSimplified);
293 assert_eq!(charset_from_unicode(0x3042), Charset::ShiftJis);
294 assert_eq!(charset_from_unicode(0xAC00), Charset::Hangul);
295 assert_eq!(charset_from_unicode(0x0E01), Charset::Thai);
296 assert_eq!(charset_from_unicode(0x03B1), Charset::Greek);
297 assert_eq!(charset_from_unicode(0x0627), Charset::Arabic);
298 assert_eq!(charset_from_unicode(0x05D0), Charset::Hebrew);
299 assert_eq!(charset_from_unicode(0x0410), Charset::Cyrillic);
300 assert_eq!(charset_from_unicode(0x0100), Charset::EasternEuropean);
301 assert_eq!(charset_from_unicode(0x1E00), Charset::Vietnamese);
302 assert_eq!(charset_from_unicode(0x10000), Charset::Ansi);
303 }
304
305 #[test]
306 fn pitch_families_derive_from_both_sources() {
307 use super::super::StandardFont;
308 assert_eq!(
309 PitchFamily::from_standard_font(StandardFont::Courier).0,
310 PitchFamily::FIXED
311 );
312 assert_eq!(
313 PitchFamily::from_standard_font(StandardFont::Helvetica).0,
314 0
315 );
316 assert_eq!(
317 PitchFamily::from_standard_font(StandardFont::Times).0,
318 PitchFamily::ROMAN
319 );
320 assert_eq!(
322 PitchFamily::from_standard_font(StandardFont::Symbol).0,
323 PitchFamily::ROMAN
324 );
325
326 let flags = FontFlags::SERIF | FontFlags::FIXED_PITCH;
327 let p = PitchFamily::from_flags(flags);
328 assert!(p.has(PitchFamily::ROMAN));
329 assert!(p.has(PitchFamily::FIXED));
330 assert!(!p.has(PitchFamily::SCRIPT));
331 }
332
333 #[cfg(all(feature = "system-fonts", not(target_arch = "wasm32")))]
334 #[test]
335 fn code_page_bits_map_to_charsets() {
336 assert_eq!(charset_for_code_page_bit(17), Some(Charset::ShiftJis));
337 assert_eq!(charset_for_code_page_bit(31), Some(Charset::Symbol));
338 assert_eq!(charset_for_code_page_bit(0), None);
339 assert_eq!(charset_for_code_page_bit(29), None);
340 }
341
342 #[test]
343 fn default_face_names_match_the_linux_table() {
344 assert_eq!(default_face_name(Charset::Ansi), "Helvetica");
345 assert_eq!(default_face_name(Charset::ChineseSimplified), "SimSun");
346 assert_eq!(default_face_name(Charset::ShiftJis), "MS Gothic");
347 assert_eq!(default_face_name(Charset::Symbol), "Arial Unicode MS");
348 }
349}