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::{
20 corety::{AzString, U8Vec},
21 format_rust_code::{FormatAsRustCode, GetHash},
22 props::{
23 basic::{
24 error::{InvalidValueErr, InvalidValueErrOwned},
25 pixel::{
26 parse_pixel_value, CssPixelValueParseError, CssPixelValueParseErrorOwned,
27 PixelValue,
28 },
29 },
30 formatter::PrintAsCssValue,
31 },
32};
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
38#[repr(C)]
39pub enum StyleFontWeight {
40 Lighter,
41 W100,
42 W200,
43 W300,
44 Normal,
45 W500,
46 W600,
47 Bold,
48 W800,
49 W900,
50 Bolder,
51}
52
53impl Default for StyleFontWeight {
54 fn default() -> Self {
55 StyleFontWeight::Normal
56 }
57}
58
59impl PrintAsCssValue for StyleFontWeight {
60 fn print_as_css_value(&self) -> String {
61 match self {
62 StyleFontWeight::Lighter => "lighter".to_string(),
63 StyleFontWeight::W100 => "100".to_string(),
64 StyleFontWeight::W200 => "200".to_string(),
65 StyleFontWeight::W300 => "300".to_string(),
66 StyleFontWeight::Normal => "normal".to_string(),
67 StyleFontWeight::W500 => "500".to_string(),
68 StyleFontWeight::W600 => "600".to_string(),
69 StyleFontWeight::Bold => "bold".to_string(),
70 StyleFontWeight::W800 => "800".to_string(),
71 StyleFontWeight::W900 => "900".to_string(),
72 StyleFontWeight::Bolder => "bolder".to_string(),
73 }
74 }
75}
76
77impl crate::format_rust_code::FormatAsRustCode for StyleFontWeight {
78 fn format_as_rust_code(&self, _tabs: usize) -> String {
79 use StyleFontWeight::*;
80 format!(
81 "StyleFontWeight::{}",
82 match self {
83 Lighter => "Lighter",
84 W100 => "W100",
85 W200 => "W200",
86 W300 => "W300",
87 Normal => "Normal",
88 W500 => "W500",
89 W600 => "W600",
90 Bold => "Bold",
91 W800 => "W800",
92 W900 => "W900",
93 Bolder => "Bolder",
94 }
95 )
96 }
97}
98
99impl StyleFontWeight {
100 pub const fn to_fc_weight(self) -> i32 {
102 match self {
103 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, }
115 }
116}
117
118#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
122#[repr(C)]
123pub enum StyleFontStyle {
124 Normal,
125 Italic,
126 Oblique,
127}
128
129impl Default for StyleFontStyle {
130 fn default() -> Self {
131 StyleFontStyle::Normal
132 }
133}
134
135impl PrintAsCssValue for StyleFontStyle {
136 fn print_as_css_value(&self) -> String {
137 match self {
138 StyleFontStyle::Normal => "normal".to_string(),
139 StyleFontStyle::Italic => "italic".to_string(),
140 StyleFontStyle::Oblique => "oblique".to_string(),
141 }
142 }
143}
144
145impl crate::format_rust_code::FormatAsRustCode for StyleFontStyle {
146 fn format_as_rust_code(&self, _tabs: usize) -> String {
147 use StyleFontStyle::*;
148 format!(
149 "StyleFontStyle::{}",
150 match self {
151 Normal => "Normal",
152 Italic => "Italic",
153 Oblique => "Oblique",
154 }
155 )
156 }
157}
158
159#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
163#[repr(C)]
164pub struct StyleFontSize {
165 pub inner: PixelValue,
166}
167
168impl Default for StyleFontSize {
169 fn default() -> Self {
170 Self {
171 inner: PixelValue::const_pt(12),
173 }
174 }
175}
176
177impl_pixel_value!(StyleFontSize);
178impl PrintAsCssValue for StyleFontSize {
179 fn print_as_css_value(&self) -> String {
180 format!("{}", self.inner)
181 }
182}
183
184pub type FontRefDestructorCallbackType = extern "C" fn(*mut c_void);
188
189#[repr(C)]
196pub struct FontRef {
197 pub parsed: *const c_void,
199 pub copies: *const AtomicUsize,
201 pub run_destructor: bool,
203 pub parsed_destructor: FontRefDestructorCallbackType,
205}
206
207impl fmt::Debug for FontRef {
208 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209 write!(f, "FontRef(0x{:x}", self.parsed as usize)?;
210 if let Some(c) = unsafe { self.copies.as_ref() } {
211 write!(f, ", copies: {})", c.load(AtomicOrdering::SeqCst))?;
212 } else {
213 write!(f, ")")?;
214 }
215 Ok(())
216 }
217}
218
219impl FontRef {
220 pub fn new(parsed: *const c_void, destructor: FontRefDestructorCallbackType) -> Self {
226 Self {
227 parsed,
228 copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
229 run_destructor: true,
230 parsed_destructor: destructor,
231 }
232 }
233
234 #[inline]
236 pub fn get_parsed(&self) -> *const c_void {
237 self.parsed
238 }
239}
240impl_option!(
241 FontRef,
242 OptionFontRef,
243 copy = false,
244 [Debug, Clone, PartialEq, Eq, Hash]
245);
246unsafe impl Send for FontRef {}
247unsafe impl Sync for FontRef {}
248impl PartialEq for FontRef {
249 fn eq(&self, rhs: &Self) -> bool {
250 self.parsed as usize == rhs.parsed as usize
251 }
252}
253impl PartialOrd for FontRef {
254 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
255 Some((self.parsed as usize).cmp(&(other.parsed as usize)))
256 }
257}
258impl Ord for FontRef {
259 fn cmp(&self, other: &Self) -> Ordering {
260 (self.parsed as usize).cmp(&(other.parsed as usize))
261 }
262}
263impl Eq for FontRef {}
264impl Hash for FontRef {
265 fn hash<H: Hasher>(&self, state: &mut H) {
266 (self.parsed as usize).hash(state);
267 }
268}
269impl Clone for FontRef {
270 fn clone(&self) -> Self {
271 if !self.copies.is_null() {
272 unsafe {
273 (*self.copies).fetch_add(1, AtomicOrdering::SeqCst);
274 }
275 }
276 Self {
277 parsed: self.parsed,
278 copies: self.copies,
279 run_destructor: true,
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 if unsafe { (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst) } == 1 {
288 unsafe {
289 (self.parsed_destructor)(self.parsed as *mut c_void);
290 let _ = Box::from_raw(self.copies as *mut AtomicUsize);
291 }
292 }
293 }
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
301#[repr(C, u8)]
302pub enum StyleFontFamily {
303 System(AzString),
304 File(AzString),
305 Ref(FontRef),
306}
307
308impl StyleFontFamily {
309 pub fn as_string(&self) -> String {
310 match &self {
311 StyleFontFamily::System(s) => {
312 let owned = s.clone().into_library_owned_string();
313 if owned.contains(char::is_whitespace) {
314 format!("\"{}\"", owned)
315 } else {
316 owned
317 }
318 }
319 StyleFontFamily::File(s) => format!("url({})", s.clone().into_library_owned_string()),
320 StyleFontFamily::Ref(s) => format!("font-ref(0x{:x})", s.parsed as usize),
321 }
322 }
323}
324
325impl_vec!(
326 StyleFontFamily,
327 StyleFontFamilyVec,
328 StyleFontFamilyVecDestructor,
329 StyleFontFamilyVecDestructorType
330);
331impl_vec_clone!(
332 StyleFontFamily,
333 StyleFontFamilyVec,
334 StyleFontFamilyVecDestructor
335);
336impl_vec_debug!(StyleFontFamily, StyleFontFamilyVec);
337impl_vec_eq!(StyleFontFamily, StyleFontFamilyVec);
338impl_vec_ord!(StyleFontFamily, StyleFontFamilyVec);
339impl_vec_hash!(StyleFontFamily, StyleFontFamilyVec);
340impl_vec_partialeq!(StyleFontFamily, StyleFontFamilyVec);
341impl_vec_partialord!(StyleFontFamily, StyleFontFamilyVec);
342
343impl PrintAsCssValue for StyleFontFamilyVec {
344 fn print_as_css_value(&self) -> String {
345 self.iter()
346 .map(|f| f.as_string())
347 .collect::<Vec<_>>()
348 .join(", ")
349 }
350}
351
352impl crate::format_rust_code::FormatAsRustCode for StyleFontFamilyVec {
354 fn format_as_rust_code(&self, _tabs: usize) -> String {
355 format!(
356 "StyleFontFamilyVec::from_const_slice(STYLE_FONT_FAMILY_{}_ITEMS)",
357 self.get_hash()
358 )
359 }
360}
361
362#[derive(Clone, PartialEq)]
367pub enum CssFontWeightParseError<'a> {
368 InvalidValue(InvalidValueErr<'a>),
369 InvalidNumber(ParseIntError),
370}
371
372impl crate::format_rust_code::FormatAsRustCode for StyleFontFamily {
374 fn format_as_rust_code(&self, _tabs: usize) -> String {
375 match self {
376 StyleFontFamily::System(id) => {
377 format!("StyleFontFamily::System(STRING_{})", id.get_hash())
378 }
379 StyleFontFamily::File(path) => {
380 format!("StyleFontFamily::File(STRING_{})", path.get_hash())
381 }
382 StyleFontFamily::Ref(font_ref) => {
383 format!("StyleFontFamily::Ref({:0x})", font_ref.parsed as usize)
384 }
385 }
386 }
387}
388impl_debug_as_display!(CssFontWeightParseError<'a>);
389impl_display! { CssFontWeightParseError<'a>, {
390 InvalidValue(e) => format!("Invalid font-weight keyword: \"{}\"", e.0),
391 InvalidNumber(e) => format!("Invalid font-weight number: {}", e),
392}}
393impl<'a> From<InvalidValueErr<'a>> for CssFontWeightParseError<'a> {
394 fn from(e: InvalidValueErr<'a>) -> Self {
395 CssFontWeightParseError::InvalidValue(e)
396 }
397}
398impl<'a> From<ParseIntError> for CssFontWeightParseError<'a> {
399 fn from(e: ParseIntError) -> Self {
400 CssFontWeightParseError::InvalidNumber(e)
401 }
402}
403
404#[derive(Debug, Clone, PartialEq)]
405pub enum CssFontWeightParseErrorOwned {
406 InvalidValue(InvalidValueErrOwned),
407 InvalidNumber(ParseIntError),
408}
409
410impl<'a> CssFontWeightParseError<'a> {
411 pub fn to_contained(&self) -> CssFontWeightParseErrorOwned {
412 match self {
413 Self::InvalidValue(e) => CssFontWeightParseErrorOwned::InvalidValue(e.to_contained()),
414 Self::InvalidNumber(e) => CssFontWeightParseErrorOwned::InvalidNumber(e.clone()),
415 }
416 }
417}
418
419impl CssFontWeightParseErrorOwned {
420 pub fn to_shared<'a>(&'a self) -> CssFontWeightParseError<'a> {
421 match self {
422 Self::InvalidValue(e) => CssFontWeightParseError::InvalidValue(e.to_shared()),
423 Self::InvalidNumber(e) => CssFontWeightParseError::InvalidNumber(e.clone()),
424 }
425 }
426}
427
428#[cfg(feature = "parser")]
429pub fn parse_font_weight<'a>(
430 input: &'a str,
431) -> Result<StyleFontWeight, CssFontWeightParseError<'a>> {
432 let input = input.trim();
433 match input {
434 "lighter" => Ok(StyleFontWeight::Lighter),
435 "normal" => Ok(StyleFontWeight::Normal),
436 "bold" => Ok(StyleFontWeight::Bold),
437 "bolder" => Ok(StyleFontWeight::Bolder),
438 "100" => Ok(StyleFontWeight::W100),
439 "200" => Ok(StyleFontWeight::W200),
440 "300" => Ok(StyleFontWeight::W300),
441 "400" => Ok(StyleFontWeight::Normal),
442 "500" => Ok(StyleFontWeight::W500),
443 "600" => Ok(StyleFontWeight::W600),
444 "700" => Ok(StyleFontWeight::Bold),
445 "800" => Ok(StyleFontWeight::W800),
446 "900" => Ok(StyleFontWeight::W900),
447 _ => Err(InvalidValueErr(input).into()),
448 }
449}
450
451#[derive(Clone, PartialEq)]
454pub enum CssFontStyleParseError<'a> {
455 InvalidValue(InvalidValueErr<'a>),
456}
457impl_debug_as_display!(CssFontStyleParseError<'a>);
458impl_display! { CssFontStyleParseError<'a>, {
459 InvalidValue(e) => format!("Invalid font-style: \"{}\"", e.0),
460}}
461impl_from! { InvalidValueErr<'a>, CssFontStyleParseError::InvalidValue }
462
463#[derive(Debug, Clone, PartialEq)]
464pub enum CssFontStyleParseErrorOwned {
465 InvalidValue(InvalidValueErrOwned),
466}
467impl<'a> CssFontStyleParseError<'a> {
468 pub fn to_contained(&self) -> CssFontStyleParseErrorOwned {
469 match self {
470 Self::InvalidValue(e) => CssFontStyleParseErrorOwned::InvalidValue(e.to_contained()),
471 }
472 }
473}
474impl CssFontStyleParseErrorOwned {
475 pub fn to_shared<'a>(&'a self) -> CssFontStyleParseError<'a> {
476 match self {
477 Self::InvalidValue(e) => CssFontStyleParseError::InvalidValue(e.to_shared()),
478 }
479 }
480}
481
482#[cfg(feature = "parser")]
483pub fn parse_font_style<'a>(input: &'a str) -> Result<StyleFontStyle, CssFontStyleParseError<'a>> {
484 match input.trim() {
485 "normal" => Ok(StyleFontStyle::Normal),
486 "italic" => Ok(StyleFontStyle::Italic),
487 "oblique" => Ok(StyleFontStyle::Oblique),
488 other => Err(InvalidValueErr(other).into()),
489 }
490}
491
492#[derive(Clone, PartialEq)]
495pub enum CssStyleFontSizeParseError<'a> {
496 PixelValue(CssPixelValueParseError<'a>),
497}
498impl_debug_as_display!(CssStyleFontSizeParseError<'a>);
499impl_display! { CssStyleFontSizeParseError<'a>, {
500 PixelValue(e) => format!("Invalid font-size: {}", e),
501}}
502impl_from! { CssPixelValueParseError<'a>, CssStyleFontSizeParseError::PixelValue }
503
504#[derive(Debug, Clone, PartialEq)]
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)]
551pub enum CssStyleFontFamilyParseErrorOwned {
552 InvalidStyleFontFamily(String),
553 UnclosedQuotes(String),
554}
555impl<'a> CssStyleFontFamilyParseError<'a> {
556 pub fn to_contained(&self) -> CssStyleFontFamilyParseErrorOwned {
557 match self {
558 CssStyleFontFamilyParseError::InvalidStyleFontFamily(s) => {
559 CssStyleFontFamilyParseErrorOwned::InvalidStyleFontFamily(s.to_string())
560 }
561 CssStyleFontFamilyParseError::UnclosedQuotes(e) => {
562 CssStyleFontFamilyParseErrorOwned::UnclosedQuotes(e.0.to_string())
563 }
564 }
565 }
566}
567impl CssStyleFontFamilyParseErrorOwned {
568 pub fn to_shared<'a>(&'a self) -> CssStyleFontFamilyParseError<'a> {
569 match self {
570 CssStyleFontFamilyParseErrorOwned::InvalidStyleFontFamily(s) => {
571 CssStyleFontFamilyParseError::InvalidStyleFontFamily(s)
572 }
573 CssStyleFontFamilyParseErrorOwned::UnclosedQuotes(s) => {
574 CssStyleFontFamilyParseError::UnclosedQuotes(UnclosedQuotesError(s))
575 }
576 }
577 }
578}
579
580#[cfg(feature = "parser")]
581pub fn parse_style_font_family<'a>(
582 input: &'a str,
583) -> Result<StyleFontFamilyVec, CssStyleFontFamilyParseError<'a>> {
584 let multiple_fonts = input.split(',');
585 let mut fonts = Vec::with_capacity(1);
586
587 for font in multiple_fonts {
588 let font = font.trim();
589 if let Ok(stripped) = strip_quotes(font) {
590 fonts.push(StyleFontFamily::System(stripped.0.to_string().into()));
591 } else {
592 fonts.push(StyleFontFamily::System(font.to_string().into()));
594 }
595 }
596
597 Ok(fonts.into())
598}
599
600use crate::corety::{OptionI16, OptionU16, OptionU32};
603
604#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
607#[repr(C)]
608pub struct Panose {
609 pub family_type: u8,
610 pub serif_style: u8,
611 pub weight: u8,
612 pub proportion: u8,
613 pub contrast: u8,
614 pub stroke_variation: u8,
615 pub arm_style: u8,
616 pub letterform: u8,
617 pub midline: u8,
618 pub x_height: u8,
619}
620
621impl Default for Panose {
622 fn default() -> Self {
623 Panose {
624 family_type: 0,
625 serif_style: 0,
626 weight: 0,
627 proportion: 0,
628 contrast: 0,
629 stroke_variation: 0,
630 arm_style: 0,
631 letterform: 0,
632 midline: 0,
633 x_height: 0,
634 }
635 }
636}
637
638impl Panose {
639 pub const fn zero() -> Self {
640 Panose {
641 family_type: 0,
642 serif_style: 0,
643 weight: 0,
644 proportion: 0,
645 contrast: 0,
646 stroke_variation: 0,
647 arm_style: 0,
648 letterform: 0,
649 midline: 0,
650 x_height: 0,
651 }
652 }
653
654 pub const fn from_array(arr: [u8; 10]) -> Self {
656 Panose {
657 family_type: arr[0],
658 serif_style: arr[1],
659 weight: arr[2],
660 proportion: arr[3],
661 contrast: arr[4],
662 stroke_variation: arr[5],
663 arm_style: arr[6],
664 letterform: arr[7],
665 midline: arr[8],
666 x_height: arr[9],
667 }
668 }
669
670 pub const fn to_array(&self) -> [u8; 10] {
672 [
673 self.family_type,
674 self.serif_style,
675 self.weight,
676 self.proportion,
677 self.contrast,
678 self.stroke_variation,
679 self.arm_style,
680 self.letterform,
681 self.midline,
682 self.x_height,
683 ]
684 }
685}
686
687#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
690#[repr(C)]
691pub struct FontMetrics {
692 pub units_per_em: u16,
694 pub font_flags: u16,
695 pub x_min: i16,
696 pub y_min: i16,
697 pub x_max: i16,
698 pub y_max: i16,
699
700 pub ascender: i16,
702 pub descender: i16,
703 pub line_gap: i16,
704 pub advance_width_max: u16,
705 pub min_left_side_bearing: i16,
706 pub min_right_side_bearing: i16,
707 pub x_max_extent: i16,
708 pub caret_slope_rise: i16,
709 pub caret_slope_run: i16,
710 pub caret_offset: i16,
711 pub num_h_metrics: u16,
712
713 pub x_avg_char_width: i16,
715 pub us_weight_class: u16,
716 pub us_width_class: u16,
717 pub fs_type: u16,
718 pub y_subscript_x_size: i16,
719 pub y_subscript_y_size: i16,
720 pub y_subscript_x_offset: i16,
721 pub y_subscript_y_offset: i16,
722 pub y_superscript_x_size: i16,
723 pub y_superscript_y_size: i16,
724 pub y_superscript_x_offset: i16,
725 pub y_superscript_y_offset: i16,
726 pub y_strikeout_size: i16,
727 pub y_strikeout_position: i16,
728 pub s_family_class: i16,
729 pub panose: Panose,
730 pub ul_unicode_range1: u32,
731 pub ul_unicode_range2: u32,
732 pub ul_unicode_range3: u32,
733 pub ul_unicode_range4: u32,
734 pub ach_vend_id: u32,
735 pub fs_selection: u16,
736 pub us_first_char_index: u16,
737 pub us_last_char_index: u16,
738
739 pub s_typo_ascender: OptionI16,
741 pub s_typo_descender: OptionI16,
742 pub s_typo_line_gap: OptionI16,
743 pub us_win_ascent: OptionU16,
744 pub us_win_descent: OptionU16,
745
746 pub ul_code_page_range1: OptionU32,
748 pub ul_code_page_range2: OptionU32,
749
750 pub sx_height: OptionI16,
752 pub s_cap_height: OptionI16,
753 pub us_default_char: OptionU16,
754 pub us_break_char: OptionU16,
755 pub us_max_context: OptionU16,
756
757 pub us_lower_optical_point_size: OptionU16,
759 pub us_upper_optical_point_size: OptionU16,
760}
761
762impl Default for FontMetrics {
763 fn default() -> Self {
764 FontMetrics::zero()
765 }
766}
767
768impl FontMetrics {
769 pub const fn zero() -> Self {
772 FontMetrics {
773 units_per_em: 1000,
774 font_flags: 0,
775 x_min: 0,
776 y_min: 0,
777 x_max: 0,
778 y_max: 0,
779 ascender: 0,
780 descender: 0,
781 line_gap: 0,
782 advance_width_max: 0,
783 min_left_side_bearing: 0,
784 min_right_side_bearing: 0,
785 x_max_extent: 0,
786 caret_slope_rise: 0,
787 caret_slope_run: 0,
788 caret_offset: 0,
789 num_h_metrics: 0,
790 x_avg_char_width: 0,
791 us_weight_class: 400,
792 us_width_class: 5,
793 fs_type: 0,
794 y_subscript_x_size: 0,
795 y_subscript_y_size: 0,
796 y_subscript_x_offset: 0,
797 y_subscript_y_offset: 0,
798 y_superscript_x_size: 0,
799 y_superscript_y_size: 0,
800 y_superscript_x_offset: 0,
801 y_superscript_y_offset: 0,
802 y_strikeout_size: 0,
803 y_strikeout_position: 0,
804 s_family_class: 0,
805 panose: Panose::zero(),
806 ul_unicode_range1: 0,
807 ul_unicode_range2: 0,
808 ul_unicode_range3: 0,
809 ul_unicode_range4: 0,
810 ach_vend_id: 0,
811 fs_selection: 0,
812 us_first_char_index: 0,
813 us_last_char_index: 0,
814 s_typo_ascender: OptionI16::None,
815 s_typo_descender: OptionI16::None,
816 s_typo_line_gap: OptionI16::None,
817 us_win_ascent: OptionU16::None,
818 us_win_descent: OptionU16::None,
819 ul_code_page_range1: OptionU32::None,
820 ul_code_page_range2: OptionU32::None,
821 sx_height: OptionI16::None,
822 s_cap_height: OptionI16::None,
823 us_default_char: OptionU16::None,
824 us_break_char: OptionU16::None,
825 us_max_context: OptionU16::None,
826 us_lower_optical_point_size: OptionU16::None,
827 us_upper_optical_point_size: OptionU16::None,
828 }
829 }
830
831 pub fn get_ascender(&self) -> i16 {
833 self.ascender
834 }
835
836 pub fn get_descender(&self) -> i16 {
838 self.descender
839 }
840
841 pub fn get_line_gap(&self) -> i16 {
843 self.line_gap
844 }
845
846 pub fn get_advance_width_max(&self) -> u16 {
848 self.advance_width_max
849 }
850
851 pub fn get_min_left_side_bearing(&self) -> i16 {
853 self.min_left_side_bearing
854 }
855
856 pub fn get_min_right_side_bearing(&self) -> i16 {
858 self.min_right_side_bearing
859 }
860
861 pub fn get_x_min(&self) -> i16 {
863 self.x_min
864 }
865
866 pub fn get_y_min(&self) -> i16 {
868 self.y_min
869 }
870
871 pub fn get_x_max(&self) -> i16 {
873 self.x_max
874 }
875
876 pub fn get_y_max(&self) -> i16 {
878 self.y_max
879 }
880
881 pub fn get_x_max_extent(&self) -> i16 {
883 self.x_max_extent
884 }
885
886 pub fn get_x_avg_char_width(&self) -> i16 {
888 self.x_avg_char_width
889 }
890
891 pub fn get_y_subscript_x_size(&self) -> i16 {
893 self.y_subscript_x_size
894 }
895
896 pub fn get_y_subscript_y_size(&self) -> i16 {
898 self.y_subscript_y_size
899 }
900
901 pub fn get_y_subscript_x_offset(&self) -> i16 {
903 self.y_subscript_x_offset
904 }
905
906 pub fn get_y_subscript_y_offset(&self) -> i16 {
908 self.y_subscript_y_offset
909 }
910
911 pub fn get_y_superscript_x_size(&self) -> i16 {
913 self.y_superscript_x_size
914 }
915
916 pub fn get_y_superscript_y_size(&self) -> i16 {
918 self.y_superscript_y_size
919 }
920
921 pub fn get_y_superscript_x_offset(&self) -> i16 {
923 self.y_superscript_x_offset
924 }
925
926 pub fn get_y_superscript_y_offset(&self) -> i16 {
928 self.y_superscript_y_offset
929 }
930
931 pub fn get_y_strikeout_size(&self) -> i16 {
933 self.y_strikeout_size
934 }
935
936 pub fn get_y_strikeout_position(&self) -> i16 {
938 self.y_strikeout_position
939 }
940
941 pub fn use_typo_metrics(&self) -> bool {
943 (self.fs_selection & 0x0080) != 0
945 }
946}
947
948#[cfg(all(test, feature = "parser"))]
949mod tests {
950 use super::*;
951
952 #[test]
953 fn test_parse_font_weight_keywords() {
954 assert_eq!(
955 parse_font_weight("normal").unwrap(),
956 StyleFontWeight::Normal
957 );
958 assert_eq!(parse_font_weight("bold").unwrap(), StyleFontWeight::Bold);
959 assert_eq!(
960 parse_font_weight("lighter").unwrap(),
961 StyleFontWeight::Lighter
962 );
963 assert_eq!(
964 parse_font_weight("bolder").unwrap(),
965 StyleFontWeight::Bolder
966 );
967 }
968
969 #[test]
970 fn test_parse_font_weight_numbers() {
971 assert_eq!(parse_font_weight("100").unwrap(), StyleFontWeight::W100);
972 assert_eq!(parse_font_weight("400").unwrap(), StyleFontWeight::Normal);
973 assert_eq!(parse_font_weight("700").unwrap(), StyleFontWeight::Bold);
974 assert_eq!(parse_font_weight("900").unwrap(), StyleFontWeight::W900);
975 }
976
977 #[test]
978 fn test_parse_font_weight_invalid() {
979 assert!(parse_font_weight("thin").is_err());
980 assert!(parse_font_weight("").is_err());
981 assert!(parse_font_weight("450").is_err());
982 assert!(parse_font_weight("boldest").is_err());
983 }
984
985 #[test]
986 fn test_parse_font_style() {
987 assert_eq!(parse_font_style("normal").unwrap(), StyleFontStyle::Normal);
988 assert_eq!(parse_font_style("italic").unwrap(), StyleFontStyle::Italic);
989 assert_eq!(
990 parse_font_style("oblique").unwrap(),
991 StyleFontStyle::Oblique
992 );
993 assert_eq!(
994 parse_font_style(" italic ").unwrap(),
995 StyleFontStyle::Italic
996 );
997 assert!(parse_font_style("slanted").is_err());
998 }
999
1000 #[test]
1001 fn test_parse_font_size() {
1002 assert_eq!(
1003 parse_style_font_size("16px").unwrap().inner,
1004 PixelValue::px(16.0)
1005 );
1006 assert_eq!(
1007 parse_style_font_size("1.2em").unwrap().inner,
1008 PixelValue::em(1.2)
1009 );
1010 assert_eq!(
1011 parse_style_font_size("12pt").unwrap().inner,
1012 PixelValue::pt(12.0)
1013 );
1014 assert_eq!(
1015 parse_style_font_size("120%").unwrap().inner,
1016 PixelValue::percent(120.0)
1017 );
1018 assert!(parse_style_font_size("medium").is_err());
1019 }
1020
1021 #[test]
1022 fn test_parse_font_family() {
1023 let result = parse_style_font_family("Arial").unwrap();
1025 assert_eq!(result.len(), 1);
1026 assert_eq!(
1027 result.as_slice()[0],
1028 StyleFontFamily::System("Arial".into())
1029 );
1030
1031 let result = parse_style_font_family("\"Times New Roman\"").unwrap();
1033 assert_eq!(result.len(), 1);
1034 assert_eq!(
1035 result.as_slice()[0],
1036 StyleFontFamily::System("Times New Roman".into())
1037 );
1038
1039 let result = parse_style_font_family("Georgia, serif").unwrap();
1041 assert_eq!(result.len(), 2);
1042 assert_eq!(
1043 result.as_slice()[0],
1044 StyleFontFamily::System("Georgia".into())
1045 );
1046 assert_eq!(
1047 result.as_slice()[1],
1048 StyleFontFamily::System("serif".into())
1049 );
1050
1051 let result = parse_style_font_family(" 'Courier New' , monospace ").unwrap();
1053 assert_eq!(result.len(), 2);
1054 assert_eq!(
1055 result.as_slice()[0],
1056 StyleFontFamily::System("Courier New".into())
1057 );
1058 assert_eq!(
1059 result.as_slice()[1],
1060 StyleFontFamily::System("monospace".into())
1061 );
1062 }
1063}