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