1use alloc::{
8 boxed::Box,
9 string::{String, ToString},
10 vec::Vec,
11};
12use core::{
13 cmp::Ordering,
14 ffi::c_void,
15 fmt,
16 hash::{Hash, Hasher},
17 num::ParseIntError,
18 sync::atomic::{AtomicUsize, Ordering as AtomicOrdering},
19};
20
21#[cfg(feature = "parser")]
22use crate::props::basic::parse::{strip_quotes, UnclosedQuotesError};
23use crate::system::SystemFontType;
24use crate::{
25 corety::{AzString, U8Vec},
26 format_rust_code::{FormatAsRustCode, GetHash},
27 props::{
28 basic::{
29 error::{InvalidValueErr, InvalidValueErrOwned},
30 pixel::{
31 parse_pixel_value, CssPixelValueParseError, CssPixelValueParseErrorOwned,
32 PixelValue,
33 },
34 },
35 formatter::PrintAsCssValue,
36 },
37};
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43#[repr(C)]
44#[derive(Default)]
45pub enum StyleFontWeight {
46 Lighter,
47 W100,
48 W200,
49 W300,
50 #[default]
51 Normal,
52 W500,
53 W600,
54 Bold,
55 W800,
56 W900,
57 Bolder,
58}
59
60
61impl PrintAsCssValue for StyleFontWeight {
62 fn print_as_css_value(&self) -> String {
63 match self {
64 StyleFontWeight::Lighter => "lighter".to_string(),
65 StyleFontWeight::W100 => "100".to_string(),
66 StyleFontWeight::W200 => "200".to_string(),
67 StyleFontWeight::W300 => "300".to_string(),
68 StyleFontWeight::Normal => "normal".to_string(),
69 StyleFontWeight::W500 => "500".to_string(),
70 StyleFontWeight::W600 => "600".to_string(),
71 StyleFontWeight::Bold => "bold".to_string(),
72 StyleFontWeight::W800 => "800".to_string(),
73 StyleFontWeight::W900 => "900".to_string(),
74 StyleFontWeight::Bolder => "bolder".to_string(),
75 }
76 }
77}
78
79impl crate::format_rust_code::FormatAsRustCode for StyleFontWeight {
80 fn format_as_rust_code(&self, _tabs: usize) -> String {
81 use StyleFontWeight::*;
82 format!(
83 "StyleFontWeight::{}",
84 match self {
85 Lighter => "Lighter",
86 W100 => "W100",
87 W200 => "W200",
88 W300 => "W300",
89 Normal => "Normal",
90 W500 => "W500",
91 W600 => "W600",
92 Bold => "Bold",
93 W800 => "W800",
94 W900 => "W900",
95 Bolder => "Bolder",
96 }
97 )
98 }
99}
100
101#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
105#[repr(C)]
106#[derive(Default)]
107pub enum StyleFontStyle {
108 #[default]
109 Normal,
110 Italic,
111 Oblique,
112}
113
114
115impl PrintAsCssValue for StyleFontStyle {
116 fn print_as_css_value(&self) -> String {
117 match self {
118 StyleFontStyle::Normal => "normal".to_string(),
119 StyleFontStyle::Italic => "italic".to_string(),
120 StyleFontStyle::Oblique => "oblique".to_string(),
121 }
122 }
123}
124
125impl crate::format_rust_code::FormatAsRustCode for StyleFontStyle {
126 fn format_as_rust_code(&self, _tabs: usize) -> String {
127 use StyleFontStyle::*;
128 format!(
129 "StyleFontStyle::{}",
130 match self {
131 Normal => "Normal",
132 Italic => "Italic",
133 Oblique => "Oblique",
134 }
135 )
136 }
137}
138
139#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
143#[repr(C)]
144pub struct StyleFontSize {
145 pub inner: PixelValue,
146}
147
148impl Default for StyleFontSize {
149 fn default() -> Self {
150 Self {
151 inner: PixelValue::const_pt(12),
153 }
154 }
155}
156
157impl_pixel_value!(StyleFontSize);
158impl PrintAsCssValue for StyleFontSize {
159 fn print_as_css_value(&self) -> String {
160 format!("{}", self.inner)
161 }
162}
163
164pub type FontRefDestructorCallbackType = extern "C" fn(*mut c_void);
168
169#[repr(C)]
176pub struct FontRef {
177 pub parsed: *const c_void,
179 pub copies: *const AtomicUsize,
181 pub run_destructor: bool,
183 pub parsed_destructor: FontRefDestructorCallbackType,
185}
186
187impl fmt::Debug for FontRef {
188 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189 write!(f, "FontRef(0x{:x}", self.parsed as usize)?;
190 if let Some(c) = unsafe { self.copies.as_ref() } {
191 write!(f, ", copies: {})", c.load(AtomicOrdering::SeqCst))?;
192 } else {
193 write!(f, ")")?;
194 }
195 Ok(())
196 }
197}
198
199impl FontRef {
200 pub fn new(parsed: *const c_void, destructor: FontRefDestructorCallbackType) -> Self {
206 Self {
207 parsed,
208 copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
209 run_destructor: true,
210 parsed_destructor: destructor,
211 }
212 }
213
214 #[inline]
216 pub fn get_parsed(&self) -> *const c_void {
217 self.parsed
218 }
219}
220impl_option!(
221 FontRef,
222 OptionFontRef,
223 copy = false,
224 [Debug, Clone, PartialEq, Eq, Hash]
225);
226unsafe impl Send for FontRef {}
227unsafe impl Sync for FontRef {}
228impl PartialEq for FontRef {
229 fn eq(&self, rhs: &Self) -> bool {
230 std::ptr::eq(self.parsed, rhs.parsed)
231 }
232}
233impl PartialOrd for FontRef {
234 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
235 Some((self.parsed as usize).cmp(&(other.parsed as usize)))
236 }
237}
238impl Ord for FontRef {
239 fn cmp(&self, other: &Self) -> Ordering {
240 (self.parsed as usize).cmp(&(other.parsed as usize))
241 }
242}
243impl Eq for FontRef {}
244impl Hash for FontRef {
245 fn hash<H: Hasher>(&self, state: &mut H) {
246 (self.parsed as usize).hash(state);
247 }
248}
249impl Clone for FontRef {
250 fn clone(&self) -> Self {
251 if !self.copies.is_null() {
252 unsafe {
253 (*self.copies).fetch_add(1, AtomicOrdering::SeqCst);
254 }
255 }
256 Self {
257 parsed: self.parsed,
258 copies: self.copies,
259 run_destructor: self.run_destructor,
260 parsed_destructor: self.parsed_destructor,
261 }
262 }
263}
264impl Drop for FontRef {
265 fn drop(&mut self) {
266 if self.run_destructor && !self.copies.is_null()
267 && unsafe { (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst) } == 1 {
268 unsafe {
269 (self.parsed_destructor)(self.parsed as *mut c_void);
270 let _ = Box::from_raw(self.copies as *mut AtomicUsize);
271 }
272 }
273 }
274}
275
276#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
286#[repr(C, u8)]
287pub enum StyleFontFamily {
288 System(AzString),
290 SystemType(SystemFontType),
293 File(AzString),
295 Ref(FontRef),
297}
298
299impl_option!(
300 StyleFontFamily,
301 OptionStyleFontFamily,
302 copy = false,
303 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
304);
305
306impl StyleFontFamily {
307 pub fn as_string(&self) -> String {
308 match &self {
309 StyleFontFamily::System(s) => {
310 let owned = s.clone().into_library_owned_string();
311 if owned.contains(char::is_whitespace) {
312 format!("\"{}\"", owned)
313 } else {
314 owned
315 }
316 }
317 StyleFontFamily::SystemType(st) => st.as_css_str().to_string(),
318 StyleFontFamily::File(s) => format!("url({})", s.clone().into_library_owned_string()),
319 StyleFontFamily::Ref(s) => format!("font-ref(0x{:x})", s.parsed as usize),
320 }
321 }
322}
323
324impl_vec!(StyleFontFamily, StyleFontFamilyVec, StyleFontFamilyVecDestructor, StyleFontFamilyVecDestructorType, StyleFontFamilyVecSlice, OptionStyleFontFamily);
325impl_vec_clone!(
326 StyleFontFamily,
327 StyleFontFamilyVec,
328 StyleFontFamilyVecDestructor
329);
330impl_vec_debug!(StyleFontFamily, StyleFontFamilyVec);
331impl_vec_eq!(StyleFontFamily, StyleFontFamilyVec);
332impl_vec_ord!(StyleFontFamily, StyleFontFamilyVec);
333impl_vec_hash!(StyleFontFamily, StyleFontFamilyVec);
334impl_vec_partialeq!(StyleFontFamily, StyleFontFamilyVec);
335impl_vec_partialord!(StyleFontFamily, StyleFontFamilyVec);
336
337impl PrintAsCssValue for StyleFontFamilyVec {
338 fn print_as_css_value(&self) -> String {
339 self.iter()
340 .map(|f| f.as_string())
341 .collect::<Vec<_>>()
342 .join(", ")
343 }
344}
345
346impl crate::format_rust_code::FormatAsRustCode for StyleFontFamilyVec {
348 fn format_as_rust_code(&self, _tabs: usize) -> String {
349 format!(
350 "StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_{}_ITEMS)",
351 self.get_hash()
352 )
353 }
354}
355
356#[derive(Clone, PartialEq)]
361pub enum CssFontWeightParseError<'a> {
362 InvalidValue(InvalidValueErr<'a>),
363 InvalidNumber(ParseIntError),
364}
365
366impl crate::format_rust_code::FormatAsRustCode for StyleFontFamily {
368 fn format_as_rust_code(&self, _tabs: usize) -> String {
369 match self {
370 StyleFontFamily::System(id) => {
371 format!("StyleFontFamily::System(STRING_{})", id.get_hash())
372 }
373 StyleFontFamily::SystemType(st) => {
374 format!("StyleFontFamily::SystemType(SystemFontType::{:?})", st)
375 }
376 StyleFontFamily::File(path) => {
377 format!("StyleFontFamily::File(STRING_{})", path.get_hash())
378 }
379 StyleFontFamily::Ref(font_ref) => {
380 format!("StyleFontFamily::Ref({:0x})", font_ref.parsed as usize)
381 }
382 }
383 }
384}
385impl_debug_as_display!(CssFontWeightParseError<'a>);
386impl_display! { CssFontWeightParseError<'a>, {
387 InvalidValue(e) => format!("Invalid font-weight keyword: \"{}\"", e.0),
388 InvalidNumber(e) => format!("Invalid font-weight number: {}", e),
389}}
390impl<'a> From<InvalidValueErr<'a>> for CssFontWeightParseError<'a> {
391 fn from(e: InvalidValueErr<'a>) -> Self {
392 CssFontWeightParseError::InvalidValue(e)
393 }
394}
395impl<'a> From<ParseIntError> for CssFontWeightParseError<'a> {
396 fn from(e: ParseIntError) -> Self {
397 CssFontWeightParseError::InvalidNumber(e)
398 }
399}
400
401#[derive(Debug, Clone, PartialEq)]
402#[repr(C, u8)]
403pub enum CssFontWeightParseErrorOwned {
404 InvalidValue(InvalidValueErrOwned),
405 InvalidNumber(crate::props::basic::error::ParseIntError),
406}
407
408impl<'a> CssFontWeightParseError<'a> {
409 pub fn to_contained(&self) -> CssFontWeightParseErrorOwned {
410 match self {
411 Self::InvalidValue(e) => CssFontWeightParseErrorOwned::InvalidValue(e.to_contained()),
412 Self::InvalidNumber(e) => CssFontWeightParseErrorOwned::InvalidNumber(e.clone().into()),
413 }
414 }
415}
416
417impl CssFontWeightParseErrorOwned {
418 pub fn to_shared<'a>(&'a self) -> CssFontWeightParseError<'a> {
419 match self {
420 Self::InvalidValue(e) => CssFontWeightParseError::InvalidValue(e.to_shared()),
421 Self::InvalidNumber(e) => CssFontWeightParseError::InvalidNumber(e.to_std()),
422 }
423 }
424}
425
426#[cfg(feature = "parser")]
427pub fn parse_font_weight<'a>(
428 input: &'a str,
429) -> Result<StyleFontWeight, CssFontWeightParseError<'a>> {
430 let input = input.trim();
431 match input {
432 "lighter" => Ok(StyleFontWeight::Lighter),
433 "normal" => Ok(StyleFontWeight::Normal),
434 "bold" => Ok(StyleFontWeight::Bold),
435 "bolder" => Ok(StyleFontWeight::Bolder),
436 "100" => Ok(StyleFontWeight::W100),
437 "200" => Ok(StyleFontWeight::W200),
438 "300" => Ok(StyleFontWeight::W300),
439 "400" => Ok(StyleFontWeight::Normal),
440 "500" => Ok(StyleFontWeight::W500),
441 "600" => Ok(StyleFontWeight::W600),
442 "700" => Ok(StyleFontWeight::Bold),
443 "800" => Ok(StyleFontWeight::W800),
444 "900" => Ok(StyleFontWeight::W900),
445 _ => Err(InvalidValueErr(input).into()),
446 }
447}
448
449#[derive(Clone, PartialEq)]
452pub enum CssFontStyleParseError<'a> {
453 InvalidValue(InvalidValueErr<'a>),
454}
455impl_debug_as_display!(CssFontStyleParseError<'a>);
456impl_display! { CssFontStyleParseError<'a>, {
457 InvalidValue(e) => format!("Invalid font-style: \"{}\"", e.0),
458}}
459impl_from! { InvalidValueErr<'a>, CssFontStyleParseError::InvalidValue }
460
461#[derive(Debug, Clone, PartialEq)]
462#[repr(C, u8)]
463pub enum CssFontStyleParseErrorOwned {
464 InvalidValue(InvalidValueErrOwned),
465}
466impl<'a> CssFontStyleParseError<'a> {
467 pub fn to_contained(&self) -> CssFontStyleParseErrorOwned {
468 match self {
469 Self::InvalidValue(e) => CssFontStyleParseErrorOwned::InvalidValue(e.to_contained()),
470 }
471 }
472}
473impl CssFontStyleParseErrorOwned {
474 pub fn to_shared<'a>(&'a self) -> CssFontStyleParseError<'a> {
475 match self {
476 Self::InvalidValue(e) => CssFontStyleParseError::InvalidValue(e.to_shared()),
477 }
478 }
479}
480
481#[cfg(feature = "parser")]
482pub fn parse_font_style<'a>(input: &'a str) -> Result<StyleFontStyle, CssFontStyleParseError<'a>> {
483 match input.trim() {
484 "normal" => Ok(StyleFontStyle::Normal),
485 "italic" => Ok(StyleFontStyle::Italic),
486 "oblique" => Ok(StyleFontStyle::Oblique),
487 other => Err(InvalidValueErr(other).into()),
488 }
489}
490
491#[derive(Clone, PartialEq)]
494pub enum CssStyleFontSizeParseError<'a> {
495 PixelValue(CssPixelValueParseError<'a>),
496}
497impl_debug_as_display!(CssStyleFontSizeParseError<'a>);
498impl_display! { CssStyleFontSizeParseError<'a>, {
499 PixelValue(e) => format!("Invalid font-size: {}", e),
500}}
501impl_from! { CssPixelValueParseError<'a>, CssStyleFontSizeParseError::PixelValue }
502
503#[derive(Debug, Clone, PartialEq)]
504#[repr(C, u8)]
505pub enum CssStyleFontSizeParseErrorOwned {
506 PixelValue(CssPixelValueParseErrorOwned),
507}
508impl<'a> CssStyleFontSizeParseError<'a> {
509 pub fn to_contained(&self) -> CssStyleFontSizeParseErrorOwned {
510 match self {
511 Self::PixelValue(e) => CssStyleFontSizeParseErrorOwned::PixelValue(e.to_contained()),
512 }
513 }
514}
515impl CssStyleFontSizeParseErrorOwned {
516 pub fn to_shared<'a>(&'a self) -> CssStyleFontSizeParseError<'a> {
517 match self {
518 Self::PixelValue(e) => CssStyleFontSizeParseError::PixelValue(e.to_shared()),
519 }
520 }
521}
522
523#[cfg(feature = "parser")]
524pub fn parse_style_font_size<'a>(
525 input: &'a str,
526) -> Result<StyleFontSize, CssStyleFontSizeParseError<'a>> {
527 Ok(StyleFontSize {
528 inner: parse_pixel_value(input)?,
529 })
530}
531
532#[derive(PartialEq, Clone)]
535pub enum CssStyleFontFamilyParseError<'a> {
536 InvalidStyleFontFamily(&'a str),
537 UnclosedQuotes(UnclosedQuotesError<'a>),
538}
539impl_debug_as_display!(CssStyleFontFamilyParseError<'a>);
540impl_display! { CssStyleFontFamilyParseError<'a>, {
541 InvalidStyleFontFamily(val) => format!("Invalid font-family: \"{}\"", val),
542 UnclosedQuotes(val) => format!("Unclosed quotes in font-family: \"{}\"", val.0),
543}}
544impl<'a> From<UnclosedQuotesError<'a>> for CssStyleFontFamilyParseError<'a> {
545 fn from(err: UnclosedQuotesError<'a>) -> Self {
546 CssStyleFontFamilyParseError::UnclosedQuotes(err)
547 }
548}
549
550#[derive(Debug, Clone, PartialEq)]
551#[repr(C, u8)]
552pub enum CssStyleFontFamilyParseErrorOwned {
553 InvalidStyleFontFamily(AzString),
554 UnclosedQuotes(AzString),
555}
556impl<'a> CssStyleFontFamilyParseError<'a> {
557 pub fn to_contained(&self) -> CssStyleFontFamilyParseErrorOwned {
558 match self {
559 CssStyleFontFamilyParseError::InvalidStyleFontFamily(s) => {
560 CssStyleFontFamilyParseErrorOwned::InvalidStyleFontFamily(s.to_string().into())
561 }
562 CssStyleFontFamilyParseError::UnclosedQuotes(e) => {
563 CssStyleFontFamilyParseErrorOwned::UnclosedQuotes(e.0.to_string().into())
564 }
565 }
566 }
567}
568impl CssStyleFontFamilyParseErrorOwned {
569 pub fn to_shared<'a>(&'a self) -> CssStyleFontFamilyParseError<'a> {
570 match self {
571 CssStyleFontFamilyParseErrorOwned::InvalidStyleFontFamily(s) => {
572 CssStyleFontFamilyParseError::InvalidStyleFontFamily(s)
573 }
574 CssStyleFontFamilyParseErrorOwned::UnclosedQuotes(s) => {
575 CssStyleFontFamilyParseError::UnclosedQuotes(UnclosedQuotesError(s))
576 }
577 }
578 }
579}
580
581#[cfg(feature = "parser")]
582pub fn parse_style_font_family<'a>(
583 input: &'a str,
584) -> Result<StyleFontFamilyVec, CssStyleFontFamilyParseError<'a>> {
585 let multiple_fonts = input.split(',');
586 let mut fonts = Vec::with_capacity(1);
587
588 for font in multiple_fonts {
589 let font = font.trim();
590
591 if font.starts_with("system:") {
593 if let Some(system_type) = SystemFontType::from_css_str(font) {
594 fonts.push(StyleFontFamily::SystemType(system_type));
595 continue;
596 }
597 }
599
600 if let Ok(stripped) = strip_quotes(font) {
601 fonts.push(StyleFontFamily::System(stripped.0.to_string().into()));
602 } else {
603 fonts.push(StyleFontFamily::System(font.to_string().into()));
605 }
606 }
607
608 Ok(fonts.into())
609}
610
611use crate::corety::{OptionI16, OptionU16, OptionU32};
614
615#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
618#[repr(C)]
619#[derive(Default)]
620pub struct Panose {
621 pub family_type: u8,
622 pub serif_style: u8,
623 pub weight: u8,
624 pub proportion: u8,
625 pub contrast: u8,
626 pub stroke_variation: u8,
627 pub arm_style: u8,
628 pub letterform: u8,
629 pub midline: u8,
630 pub x_height: u8,
631}
632
633
634impl Panose {
635 pub const fn zero() -> Self {
636 Panose {
637 family_type: 0,
638 serif_style: 0,
639 weight: 0,
640 proportion: 0,
641 contrast: 0,
642 stroke_variation: 0,
643 arm_style: 0,
644 letterform: 0,
645 midline: 0,
646 x_height: 0,
647 }
648 }
649}
650
651#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
654#[repr(C)]
655pub struct FontMetrics {
656 pub ul_code_page_range1: OptionU32,
658 pub ul_code_page_range2: OptionU32,
659
660 pub ul_unicode_range1: u32,
662 pub ul_unicode_range2: u32,
663 pub ul_unicode_range3: u32,
664 pub ul_unicode_range4: u32,
665 pub ach_vend_id: u32,
666
667 pub s_typo_ascender: OptionI16,
669 pub s_typo_descender: OptionI16,
670 pub s_typo_line_gap: OptionI16,
671 pub us_win_ascent: OptionU16,
672 pub us_win_descent: OptionU16,
673
674 pub sx_height: OptionI16,
677 pub s_cap_height: OptionI16,
678 pub us_default_char: OptionU16,
679 pub us_break_char: OptionU16,
680 pub us_max_context: OptionU16,
681
682 pub us_lower_optical_point_size: OptionU16,
684 pub us_upper_optical_point_size: OptionU16,
685
686 pub units_per_em: u16,
688 pub font_flags: u16,
689 pub x_min: i16,
690 pub y_min: i16,
691 pub x_max: i16,
692 pub y_max: i16,
693
694 pub ascender: i16,
696 pub descender: i16,
697 pub line_gap: i16,
698 pub advance_width_max: u16,
699 pub min_left_side_bearing: i16,
700 pub min_right_side_bearing: i16,
701 pub x_max_extent: i16,
702 pub caret_slope_rise: i16,
703 pub caret_slope_run: i16,
704 pub caret_offset: i16,
705 pub num_h_metrics: u16,
706
707 pub x_avg_char_width: i16,
709 pub us_weight_class: u16,
710 pub us_width_class: u16,
711 pub fs_type: u16,
712 pub y_subscript_x_size: i16,
713 pub y_subscript_y_size: i16,
714 pub y_subscript_x_offset: i16,
715 pub y_subscript_y_offset: i16,
716 pub y_superscript_x_size: i16,
717 pub y_superscript_y_size: i16,
718 pub y_superscript_x_offset: i16,
719 pub y_superscript_y_offset: i16,
720 pub y_strikeout_size: i16,
721 pub y_strikeout_position: i16,
722 pub s_family_class: i16,
723 pub fs_selection: u16,
724 pub us_first_char_index: u16,
725 pub us_last_char_index: u16,
726
727 pub panose: Panose,
729}
730
731impl Default for FontMetrics {
732 fn default() -> Self {
733 FontMetrics::zero()
734 }
735}
736
737impl FontMetrics {
738 pub const fn zero() -> Self {
741 FontMetrics {
742 ul_code_page_range1: OptionU32::None,
743 ul_code_page_range2: OptionU32::None,
744 ul_unicode_range1: 0,
745 ul_unicode_range2: 0,
746 ul_unicode_range3: 0,
747 ul_unicode_range4: 0,
748 ach_vend_id: 0,
749 s_typo_ascender: OptionI16::None,
750 s_typo_descender: OptionI16::None,
751 s_typo_line_gap: OptionI16::None,
752 us_win_ascent: OptionU16::None,
753 us_win_descent: OptionU16::None,
754 sx_height: OptionI16::None,
755 s_cap_height: OptionI16::None,
756 us_default_char: OptionU16::None,
757 us_break_char: OptionU16::None,
758 us_max_context: OptionU16::None,
759 us_lower_optical_point_size: OptionU16::None,
760 us_upper_optical_point_size: OptionU16::None,
761 units_per_em: 1000,
762 font_flags: 0,
763 x_min: 0,
764 y_min: 0,
765 x_max: 0,
766 y_max: 0,
767 ascender: 0,
768 descender: 0,
769 line_gap: 0,
770 advance_width_max: 0,
771 min_left_side_bearing: 0,
772 min_right_side_bearing: 0,
773 x_max_extent: 0,
774 caret_slope_rise: 0,
775 caret_slope_run: 0,
776 caret_offset: 0,
777 num_h_metrics: 0,
778 x_avg_char_width: 0,
779 us_weight_class: 400,
780 us_width_class: 5,
781 fs_type: 0,
782 y_subscript_x_size: 0,
783 y_subscript_y_size: 0,
784 y_subscript_x_offset: 0,
785 y_subscript_y_offset: 0,
786 y_superscript_x_size: 0,
787 y_superscript_y_size: 0,
788 y_superscript_x_offset: 0,
789 y_superscript_y_offset: 0,
790 y_strikeout_size: 0,
791 y_strikeout_position: 0,
792 s_family_class: 0,
793 fs_selection: 0,
794 us_first_char_index: 0,
795 us_last_char_index: 0,
796 panose: Panose::zero(),
797 }
798 }
799
800 pub fn get_ascender(&self) -> i16 {
802 self.ascender
803 }
804
805 pub fn get_descender(&self) -> i16 {
807 self.descender
808 }
809
810 pub fn get_line_gap(&self) -> i16 {
812 self.line_gap
813 }
814
815 pub fn get_advance_width_max(&self) -> u16 {
817 self.advance_width_max
818 }
819
820 pub fn get_min_left_side_bearing(&self) -> i16 {
822 self.min_left_side_bearing
823 }
824
825 pub fn get_min_right_side_bearing(&self) -> i16 {
827 self.min_right_side_bearing
828 }
829
830 pub fn get_x_min(&self) -> i16 {
832 self.x_min
833 }
834
835 pub fn get_y_min(&self) -> i16 {
837 self.y_min
838 }
839
840 pub fn get_x_max(&self) -> i16 {
842 self.x_max
843 }
844
845 pub fn get_y_max(&self) -> i16 {
847 self.y_max
848 }
849
850 pub fn get_x_max_extent(&self) -> i16 {
852 self.x_max_extent
853 }
854
855 pub fn get_x_avg_char_width(&self) -> i16 {
857 self.x_avg_char_width
858 }
859
860 pub fn get_y_subscript_x_size(&self) -> i16 {
862 self.y_subscript_x_size
863 }
864
865 pub fn get_y_subscript_y_size(&self) -> i16 {
867 self.y_subscript_y_size
868 }
869
870 pub fn get_y_subscript_x_offset(&self) -> i16 {
872 self.y_subscript_x_offset
873 }
874
875 pub fn get_y_subscript_y_offset(&self) -> i16 {
877 self.y_subscript_y_offset
878 }
879
880 pub fn get_y_superscript_x_size(&self) -> i16 {
882 self.y_superscript_x_size
883 }
884
885 pub fn get_y_superscript_y_size(&self) -> i16 {
887 self.y_superscript_y_size
888 }
889
890 pub fn get_y_superscript_x_offset(&self) -> i16 {
892 self.y_superscript_x_offset
893 }
894
895 pub fn get_y_superscript_y_offset(&self) -> i16 {
897 self.y_superscript_y_offset
898 }
899
900 pub fn get_y_strikeout_size(&self) -> i16 {
902 self.y_strikeout_size
903 }
904
905 pub fn get_y_strikeout_position(&self) -> i16 {
907 self.y_strikeout_position
908 }
909
910 pub fn use_typo_metrics(&self) -> bool {
912 (self.fs_selection & 0x0080) != 0
914 }
915}
916
917#[cfg(all(test, feature = "parser"))]
918mod tests {
919 use super::*;
920
921 #[test]
922 fn test_parse_font_weight_keywords() {
923 assert_eq!(
924 parse_font_weight("normal").unwrap(),
925 StyleFontWeight::Normal
926 );
927 assert_eq!(parse_font_weight("bold").unwrap(), StyleFontWeight::Bold);
928 assert_eq!(
929 parse_font_weight("lighter").unwrap(),
930 StyleFontWeight::Lighter
931 );
932 assert_eq!(
933 parse_font_weight("bolder").unwrap(),
934 StyleFontWeight::Bolder
935 );
936 }
937
938 #[test]
939 fn test_parse_font_weight_numbers() {
940 assert_eq!(parse_font_weight("100").unwrap(), StyleFontWeight::W100);
941 assert_eq!(parse_font_weight("400").unwrap(), StyleFontWeight::Normal);
942 assert_eq!(parse_font_weight("700").unwrap(), StyleFontWeight::Bold);
943 assert_eq!(parse_font_weight("900").unwrap(), StyleFontWeight::W900);
944 }
945
946 #[test]
947 fn test_parse_font_weight_invalid() {
948 assert!(parse_font_weight("thin").is_err());
949 assert!(parse_font_weight("").is_err());
950 assert!(parse_font_weight("450").is_err());
951 assert!(parse_font_weight("boldest").is_err());
952 }
953
954 #[test]
955 fn test_parse_font_style() {
956 assert_eq!(parse_font_style("normal").unwrap(), StyleFontStyle::Normal);
957 assert_eq!(parse_font_style("italic").unwrap(), StyleFontStyle::Italic);
958 assert_eq!(
959 parse_font_style("oblique").unwrap(),
960 StyleFontStyle::Oblique
961 );
962 assert_eq!(
963 parse_font_style(" italic ").unwrap(),
964 StyleFontStyle::Italic
965 );
966 assert!(parse_font_style("slanted").is_err());
967 }
968
969 #[test]
970 fn test_parse_font_size() {
971 assert_eq!(
972 parse_style_font_size("16px").unwrap().inner,
973 PixelValue::px(16.0)
974 );
975 assert_eq!(
976 parse_style_font_size("1.2em").unwrap().inner,
977 PixelValue::em(1.2)
978 );
979 assert_eq!(
980 parse_style_font_size("12pt").unwrap().inner,
981 PixelValue::pt(12.0)
982 );
983 assert_eq!(
984 parse_style_font_size("120%").unwrap().inner,
985 PixelValue::percent(120.0)
986 );
987 assert!(parse_style_font_size("medium").is_err());
988 }
989
990 #[test]
991 fn test_parse_font_family() {
992 let result = parse_style_font_family("Arial").unwrap();
994 assert_eq!(result.len(), 1);
995 assert_eq!(
996 result.as_slice()[0],
997 StyleFontFamily::System("Arial".into())
998 );
999
1000 let result = parse_style_font_family("\"Times New Roman\"").unwrap();
1002 assert_eq!(result.len(), 1);
1003 assert_eq!(
1004 result.as_slice()[0],
1005 StyleFontFamily::System("Times New Roman".into())
1006 );
1007
1008 let result = parse_style_font_family("Georgia, serif").unwrap();
1010 assert_eq!(result.len(), 2);
1011 assert_eq!(
1012 result.as_slice()[0],
1013 StyleFontFamily::System("Georgia".into())
1014 );
1015 assert_eq!(
1016 result.as_slice()[1],
1017 StyleFontFamily::System("serif".into())
1018 );
1019
1020 let result = parse_style_font_family(" 'Courier New' , monospace ").unwrap();
1022 assert_eq!(result.len(), 2);
1023 assert_eq!(
1024 result.as_slice()[0],
1025 StyleFontFamily::System("Courier New".into())
1026 );
1027 assert_eq!(
1028 result.as_slice()[1],
1029 StyleFontFamily::System("monospace".into())
1030 );
1031 }
1032
1033 #[test]
1034 fn test_parse_system_font_type() {
1035 use crate::system::SystemFontType;
1036
1037 let result = parse_style_font_family("system:ui").unwrap();
1039 assert_eq!(result.len(), 1);
1040 assert_eq!(result.as_slice()[0], StyleFontFamily::SystemType(SystemFontType::Ui));
1041
1042 let result = parse_style_font_family("system:monospace:bold").unwrap();
1044 assert_eq!(result.len(), 1);
1045 assert_eq!(result.as_slice()[0], StyleFontFamily::SystemType(SystemFontType::MonospaceBold));
1046
1047 let result = parse_style_font_family("system:monospace:italic").unwrap();
1049 assert_eq!(result.len(), 1);
1050 assert_eq!(result.as_slice()[0], StyleFontFamily::SystemType(SystemFontType::MonospaceItalic));
1051
1052 let result = parse_style_font_family("system:ui, Arial, sans-serif").unwrap();
1054 assert_eq!(result.len(), 3);
1055 assert_eq!(result.as_slice()[0], StyleFontFamily::SystemType(SystemFontType::Ui));
1056 assert_eq!(result.as_slice()[1], StyleFontFamily::System("Arial".into()));
1057 assert_eq!(result.as_slice()[2], StyleFontFamily::System("sans-serif".into()));
1058
1059 assert!(parse_style_font_family("system:ui").is_ok());
1061 assert!(parse_style_font_family("system:ui:bold").is_ok());
1062 assert!(parse_style_font_family("system:monospace").is_ok());
1063 assert!(parse_style_font_family("system:monospace:bold").is_ok());
1064 assert!(parse_style_font_family("system:monospace:italic").is_ok());
1065 assert!(parse_style_font_family("system:title").is_ok());
1066 assert!(parse_style_font_family("system:title:bold").is_ok());
1067 assert!(parse_style_font_family("system:menu").is_ok());
1068 assert!(parse_style_font_family("system:small").is_ok());
1069 assert!(parse_style_font_family("system:serif").is_ok());
1070 assert!(parse_style_font_family("system:serif:bold").is_ok());
1071
1072 let result = parse_style_font_family("system:invalid").unwrap();
1074 assert_eq!(result.len(), 1);
1075 assert_eq!(result.as_slice()[0], StyleFontFamily::System("system:invalid".into()));
1076 }
1077
1078 #[test]
1079 fn test_system_font_type_css_roundtrip() {
1080 use crate::system::SystemFontType;
1081
1082 let types = [
1084 SystemFontType::Ui,
1085 SystemFontType::UiBold,
1086 SystemFontType::Monospace,
1087 SystemFontType::MonospaceBold,
1088 SystemFontType::MonospaceItalic,
1089 SystemFontType::Title,
1090 SystemFontType::TitleBold,
1091 SystemFontType::Menu,
1092 SystemFontType::Small,
1093 SystemFontType::Serif,
1094 SystemFontType::SerifBold,
1095 ];
1096
1097 for ft in &types {
1098 let css = ft.as_css_str();
1099 let parsed = SystemFontType::from_css_str(css).unwrap();
1100 assert_eq!(*ft, parsed, "Roundtrip failed for {:?}", ft);
1101 }
1102 }
1103}