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::{AtomicU64, 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 codegen::format::{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 Self::Lighter => "lighter".to_string(),
65 Self::W100 => "100".to_string(),
66 Self::W200 => "200".to_string(),
67 Self::W300 => "300".to_string(),
68 Self::Normal => "normal".to_string(),
69 Self::W500 => "500".to_string(),
70 Self::W600 => "600".to_string(),
71 Self::Bold => "bold".to_string(),
72 Self::W800 => "800".to_string(),
73 Self::W900 => "900".to_string(),
74 Self::Bolder => "bolder".to_string(),
75 }
76 }
77}
78
79impl crate::codegen::format::FormatAsRustCode for StyleFontWeight {
80 fn format_as_rust_code(&self, _tabs: usize) -> String {
81 use StyleFontWeight::{Lighter, W100, W200, W300, Normal, W500, W600, Bold, W800, W900, Bolder};
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 Self::Normal => "normal".to_string(),
119 Self::Italic => "italic".to_string(),
120 Self::Oblique => "oblique".to_string(),
121 }
122 }
123}
124
125impl crate::codegen::format::FormatAsRustCode for StyleFontStyle {
126 fn format_as_rust_code(&self, _tabs: usize) -> String {
127 use StyleFontStyle::{Normal, Italic, Oblique};
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 id: u64,
188 pub run_destructor: bool,
190 pub parsed_destructor: FontRefDestructorCallbackType,
192}
193
194static FONT_REF_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
197
198#[must_use]
199fn next_font_ref_id() -> u64 {
200 FONT_REF_ID_COUNTER.fetch_add(1, AtomicOrdering::SeqCst)
201}
202
203impl fmt::Debug for FontRef {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 write!(f, "FontRef(0x{:x}", self.parsed as usize)?;
206 if let Some(c) = unsafe { self.copies.as_ref() } {
207 write!(f, ", copies: {})", c.load(AtomicOrdering::SeqCst))?;
208 } else {
209 write!(f, ")")?;
210 }
211 Ok(())
212 }
213}
214
215impl FontRef {
216 pub fn new(parsed: *const c_void, destructor: FontRefDestructorCallbackType) -> Self {
222 Self {
223 parsed,
224 copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
225 id: next_font_ref_id(),
226 run_destructor: true,
227 parsed_destructor: destructor,
228 }
229 }
230
231 #[inline]
233 #[must_use] pub const fn get_parsed(&self) -> *const c_void {
234 self.parsed
235 }
236}
237impl_option!(
238 FontRef,
239 OptionFontRef,
240 copy = false,
241 [Debug, Clone, PartialEq, Eq, Hash]
242);
243unsafe impl Send for FontRef {}
244unsafe impl Sync for FontRef {}
245impl PartialEq for FontRef {
248 fn eq(&self, rhs: &Self) -> bool {
249 self.id == rhs.id
250 }
251}
252impl PartialOrd for FontRef {
253 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
254 Some(self.id.cmp(&other.id))
255 }
256}
257impl Ord for FontRef {
258 fn cmp(&self, other: &Self) -> Ordering {
259 self.id.cmp(&other.id)
260 }
261}
262impl Eq for FontRef {}
263impl Hash for FontRef {
264 fn hash<H: Hasher>(&self, state: &mut H) {
265 self.id.hash(state);
266 }
267}
268impl Clone for FontRef {
269 fn clone(&self) -> Self {
270 if !self.copies.is_null() {
271 unsafe {
272 (*self.copies).fetch_add(1, AtomicOrdering::SeqCst);
273 }
274 }
275 Self {
276 parsed: self.parsed,
277 copies: self.copies,
278 id: self.id, run_destructor: self.run_destructor,
280 parsed_destructor: self.parsed_destructor,
281 }
282 }
283}
284impl Drop for FontRef {
285 fn drop(&mut self) {
286 if self.run_destructor && !self.copies.is_null()
287 && unsafe { (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst) } == 1 {
288 unsafe {
289 (self.parsed_destructor)(self.parsed.cast_mut());
290 drop(Box::from_raw(self.copies.cast_mut()));
291 }
292 }
293 }
294}
295
296#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
306#[repr(C, u8)]
307pub enum StyleFontFamily {
308 System(AzString),
310 SystemType(SystemFontType),
313 File(AzString),
315 Ref(FontRef),
317}
318
319impl_option!(
320 StyleFontFamily,
321 OptionStyleFontFamily,
322 copy = false,
323 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
324);
325
326impl StyleFontFamily {
327 pub fn as_string(&self) -> String {
328 match &self {
329 Self::System(s) => {
330 let owned = s.clone().into_library_owned_string();
331 if owned.contains(char::is_whitespace) {
332 format!("\"{owned}\"")
333 } else {
334 owned
335 }
336 }
337 Self::SystemType(st) => st.as_css_str().to_string(),
338 Self::File(s) => format!("url({})", s.clone().into_library_owned_string()),
339 Self::Ref(s) => format!("font-ref(0x{:x})", s.parsed as usize),
340 }
341 }
342}
343
344impl_vec!(StyleFontFamily, StyleFontFamilyVec, StyleFontFamilyVecDestructor, StyleFontFamilyVecDestructorType, StyleFontFamilyVecSlice, OptionStyleFontFamily);
345impl_vec_clone!(
346 StyleFontFamily,
347 StyleFontFamilyVec,
348 StyleFontFamilyVecDestructor
349);
350impl_vec_debug!(StyleFontFamily, StyleFontFamilyVec);
351impl_vec_eq!(StyleFontFamily, StyleFontFamilyVec);
352impl_vec_ord!(StyleFontFamily, StyleFontFamilyVec);
353impl_vec_hash!(StyleFontFamily, StyleFontFamilyVec);
354impl_vec_partialeq!(StyleFontFamily, StyleFontFamilyVec);
355impl_vec_partialord!(StyleFontFamily, StyleFontFamilyVec);
356
357impl PrintAsCssValue for StyleFontFamilyVec {
358 fn print_as_css_value(&self) -> String {
359 self.iter()
360 .map(StyleFontFamily::as_string)
361 .collect::<Vec<_>>()
362 .join(", ")
363 }
364}
365
366impl crate::codegen::format::FormatAsRustCode for StyleFontFamilyVec {
368 fn format_as_rust_code(&self, _tabs: usize) -> String {
369 format!(
370 "StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_{}_ITEMS)",
371 self.get_hash()
372 )
373 }
374}
375
376#[derive(Clone, PartialEq, Eq)]
381pub enum CssFontWeightParseError<'a> {
382 InvalidValue(InvalidValueErr<'a>),
383 InvalidNumber(ParseIntError),
384}
385
386impl crate::codegen::format::FormatAsRustCode for StyleFontFamily {
388 fn format_as_rust_code(&self, _tabs: usize) -> String {
389 match self {
390 Self::System(id) => {
391 format!("StyleFontFamily::System(STRING_{})", id.get_hash())
392 }
393 Self::SystemType(st) => {
394 format!("StyleFontFamily::SystemType(SystemFontType::{st:?})")
395 }
396 Self::File(path) => {
397 format!("StyleFontFamily::File(STRING_{})", path.get_hash())
398 }
399 Self::Ref(font_ref) => {
400 format!("StyleFontFamily::Ref({:0x})", font_ref.parsed as usize)
401 }
402 }
403 }
404}
405impl_debug_as_display!(CssFontWeightParseError<'a>);
406impl_display! { CssFontWeightParseError<'a>, {
407 InvalidValue(e) => format!("Invalid font-weight keyword: \"{}\"", e.0),
408 InvalidNumber(e) => format!("Invalid font-weight number: {}", e),
409}}
410impl<'a> From<InvalidValueErr<'a>> for CssFontWeightParseError<'a> {
411 fn from(e: InvalidValueErr<'a>) -> Self {
412 CssFontWeightParseError::InvalidValue(e)
413 }
414}
415impl From<ParseIntError> for CssFontWeightParseError<'_> {
416 fn from(e: ParseIntError) -> Self {
417 CssFontWeightParseError::InvalidNumber(e)
418 }
419}
420#[allow(variant_size_differences)] #[derive(Debug, Clone, PartialEq, Eq)]
422#[repr(C, u8)]
423pub enum CssFontWeightParseErrorOwned {
424 InvalidValue(InvalidValueErrOwned),
425 InvalidNumber(crate::props::basic::error::ParseIntError),
426}
427
428impl CssFontWeightParseError<'_> {
429 #[must_use] pub fn to_contained(&self) -> CssFontWeightParseErrorOwned {
430 match self {
431 Self::InvalidValue(e) => CssFontWeightParseErrorOwned::InvalidValue(e.to_contained()),
432 Self::InvalidNumber(e) => CssFontWeightParseErrorOwned::InvalidNumber(e.clone().into()),
433 }
434 }
435}
436
437impl CssFontWeightParseErrorOwned {
438 #[must_use] pub fn to_shared(&self) -> CssFontWeightParseError<'_> {
439 match self {
440 Self::InvalidValue(e) => CssFontWeightParseError::InvalidValue(e.to_shared()),
441 Self::InvalidNumber(e) => CssFontWeightParseError::InvalidNumber(e.to_std()),
442 }
443 }
444}
445
446#[cfg(feature = "parser")]
447pub fn parse_font_weight(
451 input: &str,
452) -> Result<StyleFontWeight, CssFontWeightParseError<'_>> {
453 let input = input.trim();
454 match input {
455 "lighter" => Ok(StyleFontWeight::Lighter),
456 "normal" | "400" => Ok(StyleFontWeight::Normal),
457 "bold" | "700" => Ok(StyleFontWeight::Bold),
458 "bolder" => Ok(StyleFontWeight::Bolder),
459 "100" => Ok(StyleFontWeight::W100),
460 "200" => Ok(StyleFontWeight::W200),
461 "300" => Ok(StyleFontWeight::W300),
462 "500" => Ok(StyleFontWeight::W500),
463 "600" => Ok(StyleFontWeight::W600),
464 "800" => Ok(StyleFontWeight::W800),
465 "900" => Ok(StyleFontWeight::W900),
466 _ => Err(InvalidValueErr(input).into()),
467 }
468}
469
470#[derive(Clone, PartialEq, Eq)]
473pub enum CssFontStyleParseError<'a> {
474 InvalidValue(InvalidValueErr<'a>),
475}
476impl_debug_as_display!(CssFontStyleParseError<'a>);
477impl_display! { CssFontStyleParseError<'a>, {
478 InvalidValue(e) => format!("Invalid font-style: \"{}\"", e.0),
479}}
480impl_from! { InvalidValueErr<'a>, CssFontStyleParseError::InvalidValue }
481
482#[derive(Debug, Clone, PartialEq, Eq)]
483#[repr(C, u8)]
484pub enum CssFontStyleParseErrorOwned {
485 InvalidValue(InvalidValueErrOwned),
486}
487impl CssFontStyleParseError<'_> {
488 #[must_use] pub fn to_contained(&self) -> CssFontStyleParseErrorOwned {
489 match self {
490 Self::InvalidValue(e) => CssFontStyleParseErrorOwned::InvalidValue(e.to_contained()),
491 }
492 }
493}
494impl CssFontStyleParseErrorOwned {
495 #[must_use] pub fn to_shared(&self) -> CssFontStyleParseError<'_> {
496 match self {
497 Self::InvalidValue(e) => CssFontStyleParseError::InvalidValue(e.to_shared()),
498 }
499 }
500}
501
502#[cfg(feature = "parser")]
503pub fn parse_font_style(input: &str) -> Result<StyleFontStyle, CssFontStyleParseError<'_>> {
507 match input.trim() {
508 "normal" => Ok(StyleFontStyle::Normal),
509 "italic" => Ok(StyleFontStyle::Italic),
510 "oblique" => Ok(StyleFontStyle::Oblique),
511 other => Err(InvalidValueErr(other).into()),
512 }
513}
514
515#[derive(Clone, PartialEq, Eq)]
518pub enum CssStyleFontSizeParseError<'a> {
519 PixelValue(CssPixelValueParseError<'a>),
520}
521impl_debug_as_display!(CssStyleFontSizeParseError<'a>);
522impl_display! { CssStyleFontSizeParseError<'a>, {
523 PixelValue(e) => format!("Invalid font-size: {}", e),
524}}
525impl_from! { CssPixelValueParseError<'a>, CssStyleFontSizeParseError::PixelValue }
526
527#[derive(Debug, Clone, PartialEq, Eq)]
528#[repr(C, u8)]
529pub enum CssStyleFontSizeParseErrorOwned {
530 PixelValue(CssPixelValueParseErrorOwned),
531}
532impl CssStyleFontSizeParseError<'_> {
533 #[must_use] pub fn to_contained(&self) -> CssStyleFontSizeParseErrorOwned {
534 match self {
535 Self::PixelValue(e) => CssStyleFontSizeParseErrorOwned::PixelValue(e.to_contained()),
536 }
537 }
538}
539impl CssStyleFontSizeParseErrorOwned {
540 #[must_use] pub fn to_shared(&self) -> CssStyleFontSizeParseError<'_> {
541 match self {
542 Self::PixelValue(e) => CssStyleFontSizeParseError::PixelValue(e.to_shared()),
543 }
544 }
545}
546
547#[cfg(feature = "parser")]
548pub fn parse_style_font_size(
552 input: &str,
553) -> Result<StyleFontSize, CssStyleFontSizeParseError<'_>> {
554 Ok(StyleFontSize {
555 inner: parse_pixel_value(input)?,
556 })
557}
558
559#[derive(PartialEq, Eq, Clone)]
562pub enum CssStyleFontFamilyParseError<'a> {
563 InvalidStyleFontFamily(&'a str),
564 UnclosedQuotes(UnclosedQuotesError<'a>),
565}
566impl_debug_as_display!(CssStyleFontFamilyParseError<'a>);
567impl_display! { CssStyleFontFamilyParseError<'a>, {
568 InvalidStyleFontFamily(val) => format!("Invalid font-family: \"{}\"", val),
569 UnclosedQuotes(val) => format!("Unclosed quotes in font-family: \"{}\"", val.0),
570}}
571impl<'a> From<UnclosedQuotesError<'a>> for CssStyleFontFamilyParseError<'a> {
572 fn from(err: UnclosedQuotesError<'a>) -> Self {
573 CssStyleFontFamilyParseError::UnclosedQuotes(err)
574 }
575}
576
577#[derive(Debug, Clone, PartialEq, Eq)]
578#[repr(C, u8)]
579pub enum CssStyleFontFamilyParseErrorOwned {
580 InvalidStyleFontFamily(AzString),
581 UnclosedQuotes(AzString),
582}
583impl CssStyleFontFamilyParseError<'_> {
584 #[must_use] pub fn to_contained(&self) -> CssStyleFontFamilyParseErrorOwned {
585 match self {
586 CssStyleFontFamilyParseError::InvalidStyleFontFamily(s) => {
587 CssStyleFontFamilyParseErrorOwned::InvalidStyleFontFamily((*s).to_string().into())
588 }
589 CssStyleFontFamilyParseError::UnclosedQuotes(e) => {
590 CssStyleFontFamilyParseErrorOwned::UnclosedQuotes(e.0.to_string().into())
591 }
592 }
593 }
594}
595impl CssStyleFontFamilyParseErrorOwned {
596 #[must_use] pub fn to_shared(&self) -> CssStyleFontFamilyParseError<'_> {
597 match self {
598 Self::InvalidStyleFontFamily(s) => {
599 CssStyleFontFamilyParseError::InvalidStyleFontFamily(s)
600 }
601 Self::UnclosedQuotes(s) => {
602 CssStyleFontFamilyParseError::UnclosedQuotes(UnclosedQuotesError(s))
603 }
604 }
605 }
606}
607
608#[cfg(feature = "parser")]
609pub fn parse_style_font_family(
613 input: &str,
614) -> Result<StyleFontFamilyVec, CssStyleFontFamilyParseError<'_>> {
615 let multiple_fonts = input.split(',');
616 let mut fonts = Vec::with_capacity(1);
617
618 for font in multiple_fonts {
619 let font = font.trim();
620
621 if font.starts_with("system:") {
623 if let Some(system_type) = SystemFontType::from_css_str(font) {
624 fonts.push(StyleFontFamily::SystemType(system_type));
625 continue;
626 }
627 }
629
630 if let Ok(stripped) = strip_quotes(font) {
631 fonts.push(StyleFontFamily::System(stripped.0.to_string().into()));
632 } else {
633 fonts.push(StyleFontFamily::System(font.to_string().into()));
635 }
636 }
637
638 Ok(fonts.into())
639}
640
641use crate::corety::{OptionI16, OptionU16, OptionU32};
644
645#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
648#[repr(C)]
649#[derive(Default)]
650pub struct Panose {
651 pub family_type: u8,
652 pub serif_style: u8,
653 pub weight: u8,
654 pub proportion: u8,
655 pub contrast: u8,
656 pub stroke_variation: u8,
657 pub arm_style: u8,
658 pub letterform: u8,
659 pub midline: u8,
660 pub x_height: u8,
661}
662
663
664impl Panose {
665 #[must_use] pub const fn zero() -> Self {
666 Self {
667 family_type: 0,
668 serif_style: 0,
669 weight: 0,
670 proportion: 0,
671 contrast: 0,
672 stroke_variation: 0,
673 arm_style: 0,
674 letterform: 0,
675 midline: 0,
676 x_height: 0,
677 }
678 }
679}
680
681#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
684#[repr(C)]
685pub struct FontMetrics {
686 pub ul_code_page_range1: OptionU32,
688 pub ul_code_page_range2: OptionU32,
689
690 pub ul_unicode_range1: u32,
692 pub ul_unicode_range2: u32,
693 pub ul_unicode_range3: u32,
694 pub ul_unicode_range4: u32,
695 pub ach_vend_id: u32,
696
697 pub s_typo_ascender: OptionI16,
699 pub s_typo_descender: OptionI16,
700 pub s_typo_line_gap: OptionI16,
701 pub us_win_ascent: OptionU16,
702 pub us_win_descent: OptionU16,
703
704 pub sx_height: OptionI16,
707 pub s_cap_height: OptionI16,
708 pub us_default_char: OptionU16,
709 pub us_break_char: OptionU16,
710 pub us_max_context: OptionU16,
711
712 pub us_lower_optical_point_size: OptionU16,
714 pub us_upper_optical_point_size: OptionU16,
715
716 pub units_per_em: u16,
718 pub font_flags: u16,
719 pub x_min: i16,
720 pub y_min: i16,
721 pub x_max: i16,
722 pub y_max: i16,
723
724 pub ascender: i16,
726 pub descender: i16,
727 pub line_gap: i16,
728 pub advance_width_max: u16,
729 pub min_left_side_bearing: i16,
730 pub min_right_side_bearing: i16,
731 pub x_max_extent: i16,
732 pub caret_slope_rise: i16,
733 pub caret_slope_run: i16,
734 pub caret_offset: i16,
735 pub num_h_metrics: u16,
736
737 pub x_avg_char_width: i16,
739 pub us_weight_class: u16,
740 pub us_width_class: u16,
741 pub fs_type: u16,
742 pub y_subscript_x_size: i16,
743 pub y_subscript_y_size: i16,
744 pub y_subscript_x_offset: i16,
745 pub y_subscript_y_offset: i16,
746 pub y_superscript_x_size: i16,
747 pub y_superscript_y_size: i16,
748 pub y_superscript_x_offset: i16,
749 pub y_superscript_y_offset: i16,
750 pub y_strikeout_size: i16,
751 pub y_strikeout_position: i16,
752 pub s_family_class: i16,
753 pub fs_selection: u16,
754 pub us_first_char_index: u16,
755 pub us_last_char_index: u16,
756
757 pub panose: Panose,
759}
760
761impl Default for FontMetrics {
762 fn default() -> Self {
763 Self::zero()
764 }
765}
766
767impl FontMetrics {
768 #[must_use] pub const fn zero() -> Self {
771 Self {
772 ul_code_page_range1: OptionU32::None,
773 ul_code_page_range2: OptionU32::None,
774 ul_unicode_range1: 0,
775 ul_unicode_range2: 0,
776 ul_unicode_range3: 0,
777 ul_unicode_range4: 0,
778 ach_vend_id: 0,
779 s_typo_ascender: OptionI16::None,
780 s_typo_descender: OptionI16::None,
781 s_typo_line_gap: OptionI16::None,
782 us_win_ascent: OptionU16::None,
783 us_win_descent: OptionU16::None,
784 sx_height: OptionI16::None,
785 s_cap_height: OptionI16::None,
786 us_default_char: OptionU16::None,
787 us_break_char: OptionU16::None,
788 us_max_context: OptionU16::None,
789 us_lower_optical_point_size: OptionU16::None,
790 us_upper_optical_point_size: OptionU16::None,
791 units_per_em: 1000,
792 font_flags: 0,
793 x_min: 0,
794 y_min: 0,
795 x_max: 0,
796 y_max: 0,
797 ascender: 0,
798 descender: 0,
799 line_gap: 0,
800 advance_width_max: 0,
801 min_left_side_bearing: 0,
802 min_right_side_bearing: 0,
803 x_max_extent: 0,
804 caret_slope_rise: 0,
805 caret_slope_run: 0,
806 caret_offset: 0,
807 num_h_metrics: 0,
808 x_avg_char_width: 0,
809 us_weight_class: 400,
810 us_width_class: 5,
811 fs_type: 0,
812 y_subscript_x_size: 0,
813 y_subscript_y_size: 0,
814 y_subscript_x_offset: 0,
815 y_subscript_y_offset: 0,
816 y_superscript_x_size: 0,
817 y_superscript_y_size: 0,
818 y_superscript_x_offset: 0,
819 y_superscript_y_offset: 0,
820 y_strikeout_size: 0,
821 y_strikeout_position: 0,
822 s_family_class: 0,
823 fs_selection: 0,
824 us_first_char_index: 0,
825 us_last_char_index: 0,
826 panose: Panose::zero(),
827 }
828 }
829
830 #[must_use] pub const fn get_ascender(&self) -> i16 {
832 self.ascender
833 }
834
835 #[must_use] pub const fn get_descender(&self) -> i16 {
837 self.descender
838 }
839
840 #[must_use] pub const fn get_line_gap(&self) -> i16 {
842 self.line_gap
843 }
844
845 #[must_use] pub const fn get_advance_width_max(&self) -> u16 {
847 self.advance_width_max
848 }
849
850 #[must_use] pub const fn get_min_left_side_bearing(&self) -> i16 {
852 self.min_left_side_bearing
853 }
854
855 #[must_use] pub const fn get_min_right_side_bearing(&self) -> i16 {
857 self.min_right_side_bearing
858 }
859
860 #[must_use] pub const fn get_x_min(&self) -> i16 {
862 self.x_min
863 }
864
865 #[must_use] pub const fn get_y_min(&self) -> i16 {
867 self.y_min
868 }
869
870 #[must_use] pub const fn get_x_max(&self) -> i16 {
872 self.x_max
873 }
874
875 #[must_use] pub const fn get_y_max(&self) -> i16 {
877 self.y_max
878 }
879
880 #[must_use] pub const fn get_x_max_extent(&self) -> i16 {
882 self.x_max_extent
883 }
884
885 #[must_use] pub const fn get_x_avg_char_width(&self) -> i16 {
887 self.x_avg_char_width
888 }
889
890 #[must_use] pub const fn get_y_subscript_x_size(&self) -> i16 {
892 self.y_subscript_x_size
893 }
894
895 #[must_use] pub const fn get_y_subscript_y_size(&self) -> i16 {
897 self.y_subscript_y_size
898 }
899
900 #[must_use] pub const fn get_y_subscript_x_offset(&self) -> i16 {
902 self.y_subscript_x_offset
903 }
904
905 #[must_use] pub const fn get_y_subscript_y_offset(&self) -> i16 {
907 self.y_subscript_y_offset
908 }
909
910 #[must_use] pub const fn get_y_superscript_x_size(&self) -> i16 {
912 self.y_superscript_x_size
913 }
914
915 #[must_use] pub const fn get_y_superscript_y_size(&self) -> i16 {
917 self.y_superscript_y_size
918 }
919
920 #[must_use] pub const fn get_y_superscript_x_offset(&self) -> i16 {
922 self.y_superscript_x_offset
923 }
924
925 #[must_use] pub const fn get_y_superscript_y_offset(&self) -> i16 {
927 self.y_superscript_y_offset
928 }
929
930 #[must_use] pub const fn get_y_strikeout_size(&self) -> i16 {
932 self.y_strikeout_size
933 }
934
935 #[must_use] pub const fn get_y_strikeout_position(&self) -> i16 {
937 self.y_strikeout_position
938 }
939
940 #[must_use] pub const fn use_typo_metrics(&self) -> bool {
942 (self.fs_selection & 0x0080) != 0
944 }
945}
946
947#[cfg(all(test, feature = "parser"))]
948mod tests {
949 use super::*;
950
951 #[test]
952 fn test_parse_font_weight_keywords() {
953 assert_eq!(
954 parse_font_weight("normal").unwrap(),
955 StyleFontWeight::Normal
956 );
957 assert_eq!(parse_font_weight("bold").unwrap(), StyleFontWeight::Bold);
958 assert_eq!(
959 parse_font_weight("lighter").unwrap(),
960 StyleFontWeight::Lighter
961 );
962 assert_eq!(
963 parse_font_weight("bolder").unwrap(),
964 StyleFontWeight::Bolder
965 );
966 }
967
968 #[test]
969 fn test_parse_font_weight_numbers() {
970 assert_eq!(parse_font_weight("100").unwrap(), StyleFontWeight::W100);
971 assert_eq!(parse_font_weight("400").unwrap(), StyleFontWeight::Normal);
972 assert_eq!(parse_font_weight("700").unwrap(), StyleFontWeight::Bold);
973 assert_eq!(parse_font_weight("900").unwrap(), StyleFontWeight::W900);
974 }
975
976 #[test]
977 fn test_parse_font_weight_invalid() {
978 assert!(parse_font_weight("thin").is_err());
979 assert!(parse_font_weight("").is_err());
980 assert!(parse_font_weight("450").is_err());
981 assert!(parse_font_weight("boldest").is_err());
982 }
983
984 #[test]
985 fn test_parse_font_style() {
986 assert_eq!(parse_font_style("normal").unwrap(), StyleFontStyle::Normal);
987 assert_eq!(parse_font_style("italic").unwrap(), StyleFontStyle::Italic);
988 assert_eq!(
989 parse_font_style("oblique").unwrap(),
990 StyleFontStyle::Oblique
991 );
992 assert_eq!(
993 parse_font_style(" italic ").unwrap(),
994 StyleFontStyle::Italic
995 );
996 assert!(parse_font_style("slanted").is_err());
997 }
998
999 #[test]
1000 fn test_parse_font_size() {
1001 assert_eq!(
1002 parse_style_font_size("16px").unwrap().inner,
1003 PixelValue::px(16.0)
1004 );
1005 assert_eq!(
1006 parse_style_font_size("1.2em").unwrap().inner,
1007 PixelValue::em(1.2)
1008 );
1009 assert_eq!(
1010 parse_style_font_size("12pt").unwrap().inner,
1011 PixelValue::pt(12.0)
1012 );
1013 assert_eq!(
1014 parse_style_font_size("120%").unwrap().inner,
1015 PixelValue::percent(120.0)
1016 );
1017 assert!(parse_style_font_size("medium").is_err());
1018 }
1019
1020 #[test]
1021 fn test_parse_font_family() {
1022 let result = parse_style_font_family("Arial").unwrap();
1024 assert_eq!(result.len(), 1);
1025 assert_eq!(
1026 result.as_slice()[0],
1027 StyleFontFamily::System("Arial".into())
1028 );
1029
1030 let result = parse_style_font_family("\"Times New Roman\"").unwrap();
1032 assert_eq!(result.len(), 1);
1033 assert_eq!(
1034 result.as_slice()[0],
1035 StyleFontFamily::System("Times New Roman".into())
1036 );
1037
1038 let result = parse_style_font_family("Georgia, serif").unwrap();
1040 assert_eq!(result.len(), 2);
1041 assert_eq!(
1042 result.as_slice()[0],
1043 StyleFontFamily::System("Georgia".into())
1044 );
1045 assert_eq!(
1046 result.as_slice()[1],
1047 StyleFontFamily::System("serif".into())
1048 );
1049
1050 let result = parse_style_font_family(" 'Courier New' , monospace ").unwrap();
1052 assert_eq!(result.len(), 2);
1053 assert_eq!(
1054 result.as_slice()[0],
1055 StyleFontFamily::System("Courier New".into())
1056 );
1057 assert_eq!(
1058 result.as_slice()[1],
1059 StyleFontFamily::System("monospace".into())
1060 );
1061 }
1062
1063 #[test]
1064 fn test_parse_system_font_type() {
1065 use crate::system::SystemFontType;
1066
1067 let result = parse_style_font_family("system:ui").unwrap();
1069 assert_eq!(result.len(), 1);
1070 assert_eq!(result.as_slice()[0], StyleFontFamily::SystemType(SystemFontType::Ui));
1071
1072 let result = parse_style_font_family("system:monospace:bold").unwrap();
1074 assert_eq!(result.len(), 1);
1075 assert_eq!(result.as_slice()[0], StyleFontFamily::SystemType(SystemFontType::MonospaceBold));
1076
1077 let result = parse_style_font_family("system:monospace:italic").unwrap();
1079 assert_eq!(result.len(), 1);
1080 assert_eq!(result.as_slice()[0], StyleFontFamily::SystemType(SystemFontType::MonospaceItalic));
1081
1082 let result = parse_style_font_family("system:ui, Arial, sans-serif").unwrap();
1084 assert_eq!(result.len(), 3);
1085 assert_eq!(result.as_slice()[0], StyleFontFamily::SystemType(SystemFontType::Ui));
1086 assert_eq!(result.as_slice()[1], StyleFontFamily::System("Arial".into()));
1087 assert_eq!(result.as_slice()[2], StyleFontFamily::System("sans-serif".into()));
1088
1089 assert!(parse_style_font_family("system:ui").is_ok());
1091 assert!(parse_style_font_family("system:ui:bold").is_ok());
1092 assert!(parse_style_font_family("system:monospace").is_ok());
1093 assert!(parse_style_font_family("system:monospace:bold").is_ok());
1094 assert!(parse_style_font_family("system:monospace:italic").is_ok());
1095 assert!(parse_style_font_family("system:title").is_ok());
1096 assert!(parse_style_font_family("system:title:bold").is_ok());
1097 assert!(parse_style_font_family("system:menu").is_ok());
1098 assert!(parse_style_font_family("system:small").is_ok());
1099 assert!(parse_style_font_family("system:serif").is_ok());
1100 assert!(parse_style_font_family("system:serif:bold").is_ok());
1101
1102 let result = parse_style_font_family("system:invalid").unwrap();
1104 assert_eq!(result.len(), 1);
1105 assert_eq!(result.as_slice()[0], StyleFontFamily::System("system:invalid".into()));
1106 }
1107
1108 #[test]
1109 fn test_system_font_type_css_roundtrip() {
1110 use crate::system::SystemFontType;
1111
1112 let types = [
1114 SystemFontType::Ui,
1115 SystemFontType::UiBold,
1116 SystemFontType::Monospace,
1117 SystemFontType::MonospaceBold,
1118 SystemFontType::MonospaceItalic,
1119 SystemFontType::Title,
1120 SystemFontType::TitleBold,
1121 SystemFontType::Menu,
1122 SystemFontType::Small,
1123 SystemFontType::Serif,
1124 SystemFontType::SerifBold,
1125 ];
1126
1127 for ft in &types {
1128 let css = ft.as_css_str();
1129 let parsed = SystemFontType::from_css_str(css).unwrap();
1130 assert_eq!(*ft, parsed, "Roundtrip failed for {ft:?}");
1131 }
1132 }
1133}