1use crate::Atom;
8use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10use crate::properties::CSSWideKeyword;
11use crate::typed_om::{ToTyped, TypedValue};
12use crate::values::animated::ToAnimatedValue;
13use crate::values::computed::{
14 Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,
15 ToComputedValue, Zoom,
16};
17use crate::values::generics::font::{
18 FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,
19};
20use crate::values::generics::{NonNegative, font as generics};
21use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
22use crate::values::specified::font::{
23 self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
24};
25use crate::values::specified::length::{FontBaseSize, LineHeightBase};
26use crate::values::{CSSInteger, StyleParseErrorKind};
27use cssparser::{CssStringWriter, Parser, serialize_identifier};
28use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
29use num_traits::abs;
30use num_traits::cast::AsPrimitive;
31use std::fmt::{self, Write};
32use style_traits::{CssWriter, ParseError, ToCss};
33use thin_vec::ThinVec;
34
35pub use crate::values::computed::Length as MozScriptMinSize;
36pub use crate::values::specified::Integer as SpecifiedInteger;
37pub use crate::values::specified::Number as SpecifiedNumber;
38pub use crate::values::specified::font::{
39 FontKerning, FontOpticalSizing, FontPalette, FontSmoothing, FontSynthesis, FontSynthesisStyle,
40 FontVariantCaps, FontVariantEmoji, FontVariantPosition, MathShift, MathStyle, MathVariant,
41 MozScriptSizeMultiplier,
42};
43pub use crate::values::specified::font::{
44 FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric,
45 QueryFontMetricsFlags, XLang, XTextScale,
46};
47
48#[repr(C)]
67#[derive(
68 Clone,
69 ComputeSquaredDistance,
70 Copy,
71 Debug,
72 Deserialize,
73 Eq,
74 Hash,
75 MallocSizeOf,
76 PartialEq,
77 PartialOrd,
78 Serialize,
79 ToResolvedValue,
80)]
81pub struct FixedPoint<T, const FRACTION_BITS: u16> {
82 pub value: T,
84}
85
86impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
87where
88 T: AsPrimitive<f32>,
89 f32: AsPrimitive<T>,
90 u16: AsPrimitive<T>,
91{
92 const SCALE: u16 = 1 << FRACTION_BITS;
93 const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;
94
95 pub fn from_float(v: f32) -> Self {
97 Self {
98 value: (v * Self::SCALE as f32).round().as_(),
99 }
100 }
101
102 pub fn to_float(&self) -> f32 {
104 self.value.as_() * Self::INVERSE_SCALE
105 }
106}
107
108impl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> {
111 type Output = Self;
112 fn div(self, rhs: Self) -> Self {
113 Self {
114 value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16,
115 }
116 }
117}
118impl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> {
119 type Output = Self;
120 fn mul(self, rhs: Self) -> Self {
121 Self {
122 value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16,
123 }
124 }
125}
126
127pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
132
133pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;
136
137#[derive(
146 Clone,
147 ComputeSquaredDistance,
148 Copy,
149 Debug,
150 Deserialize,
151 Hash,
152 MallocSizeOf,
153 PartialEq,
154 PartialOrd,
155 Serialize,
156 ToResolvedValue,
157)]
158#[repr(C)]
159pub struct FontWeight(FontWeightFixedPoint);
160impl ToAnimatedValue for FontWeight {
161 type AnimatedValue = Number;
162
163 #[inline]
164 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
165 self.value()
166 }
167
168 #[inline]
169 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
170 FontWeight::from_float(animated)
171 }
172}
173
174impl ToCss for FontWeight {
175 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
176 where
177 W: fmt::Write,
178 {
179 self.value().to_css(dest)
180 }
181}
182
183impl ToTyped for FontWeight {
184 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
185 self.value().to_typed(dest)
186 }
187}
188
189impl FontWeight {
190 pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
192 value: 400 << FONT_WEIGHT_FRACTION_BITS,
193 });
194
195 pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
197 value: 700 << FONT_WEIGHT_FRACTION_BITS,
198 });
199
200 pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
202 value: 600 << FONT_WEIGHT_FRACTION_BITS,
203 });
204
205 pub const PREFER_BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
208 value: 500 << FONT_WEIGHT_FRACTION_BITS,
209 });
210
211 pub fn normal() -> Self {
213 Self::NORMAL
214 }
215
216 pub fn is_bold(&self) -> bool {
218 *self >= Self::BOLD_THRESHOLD
219 }
220
221 pub fn value(&self) -> f32 {
223 self.0.to_float()
224 }
225
226 pub fn from_float(v: f32) -> Self {
228 Self(FixedPoint::from_float(
229 v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
230 ))
231 }
232
233 pub fn bolder(self) -> Self {
238 let value = self.value();
239 if value < 350. {
240 return Self::NORMAL;
241 }
242 if value < 550. {
243 return Self::BOLD;
244 }
245 Self::from_float(value.max(900.))
246 }
247
248 pub fn lighter(self) -> Self {
253 let value = self.value();
254 if value < 550. {
255 return Self::from_float(value.min(100.));
256 }
257 if value < 750. {
258 return Self::NORMAL;
259 }
260 Self::BOLD
261 }
262}
263
264#[derive(
265 Animate,
266 Clone,
267 ComputeSquaredDistance,
268 Copy,
269 Debug,
270 Deserialize,
271 MallocSizeOf,
272 PartialEq,
273 Serialize,
274 ToAnimatedZero,
275 ToCss,
276 ToTyped,
277)]
278pub struct FontSize {
280 pub computed_size: NonNegativeLength,
283 #[css(skip)]
286 pub used_size: NonNegativeLength,
287 #[css(skip)]
289 pub keyword_info: KeywordInfo,
290}
291
292impl FontSize {
293 #[inline]
295 pub fn computed_size(&self) -> Length {
296 self.computed_size.0
297 }
298
299 #[inline]
301 pub fn used_size(&self) -> Length {
302 self.used_size.0
303 }
304
305 #[inline]
307 pub fn zoom(&self, zoom: Zoom) -> Self {
308 Self {
309 computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
310 used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
311 keyword_info: self.keyword_info,
312 }
313 }
314
315 #[inline]
316 pub fn medium() -> Self {
318 Self {
319 computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
320 used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
321 keyword_info: KeywordInfo::medium(),
322 }
323 }
324}
325
326impl ToAnimatedValue for FontSize {
327 type AnimatedValue = Length;
328
329 #[inline]
330 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
331 self.computed_size.0.to_animated_value(context)
332 }
333
334 #[inline]
335 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
336 FontSize {
337 computed_size: NonNegative(animated.clamp_to_non_negative()),
338 used_size: NonNegative(animated.clamp_to_non_negative()),
339 keyword_info: KeywordInfo::none(),
340 }
341 }
342}
343
344impl ToResolvedValue for FontSize {
345 type ResolvedValue = NonNegativeLength;
346
347 #[inline]
348 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
349 self.computed_size.to_resolved_value(context)
350 }
351
352 #[inline]
353 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
354 let computed_size = NonNegativeLength::from_resolved_value(resolved);
355 Self {
356 computed_size,
357 used_size: computed_size,
358 keyword_info: KeywordInfo::none(),
359 }
360 }
361}
362
363#[derive(
364 Clone,
365 Debug,
366 Deserialize,
367 Eq,
368 Hash,
369 PartialEq,
370 Serialize,
371 ToComputedValue,
372 ToResolvedValue,
373 ToTyped,
374)]
375#[repr(C)]
377#[typed(todo_derive_fields)]
378pub struct FontFamily {
379 pub families: FontFamilyList,
381 pub is_system_font: bool,
383 pub is_initial: bool,
386}
387
388macro_rules! static_font_family {
389 ($ident:ident, $family:expr) => {
390 static $ident: std::sync::LazyLock<FontFamily> = std::sync::LazyLock::new(|| FontFamily {
391 families: FontFamilyList {
392 list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
393 },
394 is_system_font: false,
395 is_initial: false,
396 });
397 };
398}
399
400impl FontFamily {
401 #[inline]
402 pub fn serif() -> Self {
404 Self::generic(GenericFontFamily::Serif).clone()
405 }
406
407 #[cfg(feature = "gecko")]
409 pub(crate) fn moz_bullet() -> &'static Self {
410 static_font_family!(
411 MOZ_BULLET,
412 SingleFontFamily::FamilyName(FamilyName {
413 name: atom!("-moz-bullet-font"),
414 syntax: FontFamilyNameSyntax::Identifiers,
415 })
416 );
417
418 &MOZ_BULLET
419 }
420
421 #[cfg(feature = "gecko")]
423 pub fn for_system_font(name: &str) -> Self {
424 Self {
425 families: FontFamilyList {
426 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
427 FamilyName {
428 name: Atom::from(name),
429 syntax: FontFamilyNameSyntax::Identifiers,
430 },
431 ))),
432 },
433 is_system_font: true,
434 is_initial: false,
435 }
436 }
437
438 pub fn generic(generic: GenericFontFamily) -> &'static Self {
440 macro_rules! generic_font_family {
441 ($ident:ident, $family:ident) => {
442 static_font_family!(
443 $ident,
444 SingleFontFamily::Generic(GenericFontFamily::$family)
445 )
446 };
447 }
448
449 generic_font_family!(SERIF, Serif);
450 generic_font_family!(SANS_SERIF, SansSerif);
451 generic_font_family!(MONOSPACE, Monospace);
452 generic_font_family!(CURSIVE, Cursive);
453 generic_font_family!(FANTASY, Fantasy);
454 #[cfg(feature = "gecko")]
455 generic_font_family!(MATH, Math);
456 #[cfg(feature = "gecko")]
457 generic_font_family!(MOZ_EMOJI, MozEmoji);
458 generic_font_family!(SYSTEM_UI, SystemUi);
459
460 let family = match generic {
461 GenericFontFamily::None => {
462 debug_assert!(false, "Bogus caller!");
463 &*SERIF
464 },
465 GenericFontFamily::Serif => &*SERIF,
466 GenericFontFamily::SansSerif => &*SANS_SERIF,
467 GenericFontFamily::Monospace => &*MONOSPACE,
468 GenericFontFamily::Cursive => &*CURSIVE,
469 GenericFontFamily::Fantasy => &*FANTASY,
470 #[cfg(feature = "gecko")]
471 GenericFontFamily::Math => &*MATH,
472 #[cfg(feature = "gecko")]
473 GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
474 GenericFontFamily::SystemUi => &*SYSTEM_UI,
475 };
476 debug_assert_eq!(
477 *family.families.iter().next().unwrap(),
478 SingleFontFamily::Generic(generic)
479 );
480 family
481 }
482}
483
484impl MallocSizeOf for FontFamily {
485 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
486 use malloc_size_of::MallocUnconditionalSizeOf;
487 let shared_font_list = &self.families.list;
491 if shared_font_list.is_unique() {
492 shared_font_list.unconditional_size_of(ops)
493 } else {
494 0
495 }
496 }
497}
498
499impl ToCss for FontFamily {
500 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
501 where
502 W: fmt::Write,
503 {
504 let mut iter = self.families.iter();
505 match iter.next() {
506 Some(f) => f.to_css(dest)?,
507 None => return Ok(()),
508 }
509 for family in iter {
510 dest.write_str(", ")?;
511 family.to_css(dest)?;
512 }
513 Ok(())
514 }
515}
516
517#[derive(
519 Clone,
520 Debug,
521 Deserialize,
522 Eq,
523 Hash,
524 MallocSizeOf,
525 PartialEq,
526 Serialize,
527 ToComputedValue,
528 ToResolvedValue,
529 ToShmem,
530)]
531#[repr(C)]
532pub struct FamilyName {
533 pub name: Atom,
535 pub syntax: FontFamilyNameSyntax,
537}
538
539#[cfg(feature = "gecko")]
540impl FamilyName {
541 fn is_known_icon_font_family(&self) -> bool {
542 use crate::gecko_bindings::bindings;
543 unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
544 }
545}
546
547#[cfg(feature = "servo")]
548impl FamilyName {
549 fn is_known_icon_font_family(&self) -> bool {
550 false
551 }
552}
553
554impl ToCss for FamilyName {
555 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
556 where
557 W: fmt::Write,
558 {
559 match self.syntax {
560 FontFamilyNameSyntax::Quoted => {
561 dest.write_char('"')?;
562 write!(CssStringWriter::new(dest), "{}", self.name)?;
563 dest.write_char('"')
564 },
565 FontFamilyNameSyntax::Identifiers => {
566 let mut first = true;
567 for ident in self.name.to_string().split(' ') {
568 if first {
569 first = false;
570 } else {
571 dest.write_char(' ')?;
572 }
573 debug_assert!(
574 !ident.is_empty(),
575 "Family name with leading, \
576 trailing, or consecutive white spaces should \
577 have been marked quoted by the parser"
578 );
579 serialize_identifier(ident, dest)?;
580 }
581 Ok(())
582 },
583 }
584 }
585}
586
587#[derive(
588 Clone,
589 Copy,
590 Debug,
591 Deserialize,
592 Eq,
593 Hash,
594 MallocSizeOf,
595 PartialEq,
596 Serialize,
597 ToComputedValue,
598 ToResolvedValue,
599 ToShmem,
600)]
601#[repr(u8)]
604pub enum FontFamilyNameSyntax {
605 Quoted,
608
609 Identifiers,
612}
613
614#[derive(
617 Clone,
618 Debug,
619 Deserialize,
620 Eq,
621 Hash,
622 MallocSizeOf,
623 PartialEq,
624 Serialize,
625 ToCss,
626 ToComputedValue,
627 ToResolvedValue,
628 ToShmem,
629)]
630#[repr(u8)]
631pub enum SingleFontFamily {
632 FamilyName(FamilyName),
634 Generic(GenericFontFamily),
636}
637
638fn system_ui_enabled(_: &ParserContext) -> bool {
639 crate::pref!("layout.css.system-ui.enabled")
640}
641
642#[cfg(feature = "gecko")]
643fn math_enabled(context: &ParserContext) -> bool {
644 context.chrome_rules_enabled() || crate::pref!("mathml.font_family_math.enabled")
645}
646
647#[derive(
657 Clone,
658 Copy,
659 Debug,
660 Deserialize,
661 Eq,
662 Hash,
663 MallocSizeOf,
664 PartialEq,
665 Parse,
666 Serialize,
667 ToCss,
668 ToComputedValue,
669 ToResolvedValue,
670 ToShmem,
671)]
672#[repr(u32)]
673#[allow(missing_docs)]
674pub enum GenericFontFamily {
675 #[css(skip)]
679 None = 0,
680 Serif,
681 SansSerif,
682 #[parse(aliases = "-moz-fixed")]
683 Monospace,
684 Cursive,
685 Fantasy,
686 #[cfg(feature = "gecko")]
687 #[parse(condition = "math_enabled")]
688 Math,
689 #[parse(condition = "system_ui_enabled")]
690 SystemUi,
691 #[css(skip)]
693 #[cfg(feature = "gecko")]
694 MozEmoji,
695}
696
697impl GenericFontFamily {
698 pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
702 match self {
703 Self::None | Self::Cursive | Self::Fantasy | Self::SystemUi => false,
704 #[cfg(feature = "gecko")]
705 Self::Math | Self::MozEmoji => false,
706 Self::Serif | Self::SansSerif | Self::Monospace => true,
707 }
708 }
709}
710
711impl Parse for SingleFontFamily {
712 fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
714 let is_reserved_kw = |s: &str| -> bool {
718 CSSWideKeyword::from_ident(s).is_ok() || s.eq_ignore_ascii_case("default")
719 };
720
721 let canonical_syntax_for = |s: &str| -> FontFamilyNameSyntax {
724 let mut in_word = false;
730 for b in s.as_bytes() {
731 if in_word && *b == b' ' {
732 in_word = false;
733 continue;
734 }
735 if !in_word && b.is_ascii_digit() {
736 return FontFamilyNameSyntax::Quoted;
737 }
738 if b.is_ascii_alphanumeric() || *b == b'-' || *b == b'_' || !b.is_ascii() {
739 in_word = true;
740 continue;
741 }
742 return FontFamilyNameSyntax::Quoted;
743 }
744 if !in_word {
745 return FontFamilyNameSyntax::Quoted;
746 }
747 if is_reserved_kw(s) {
749 return FontFamilyNameSyntax::Quoted;
750 }
751 if GenericFontFamily::parse(context, &mut Parser::new(&s)).is_ok() {
753 return FontFamilyNameSyntax::Quoted;
754 }
755 FontFamilyNameSyntax::Identifiers
756 };
757
758 if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
759 return Ok(SingleFontFamily::FamilyName(FamilyName {
760 name: Atom::from(&*value),
761 syntax: canonical_syntax_for(&value),
762 }));
763 }
764
765 if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
766 return Ok(SingleFontFamily::Generic(generic));
767 }
768
769 let first_ident = input.expect_ident_cloned()?;
770 let mut value = first_ident.as_ref().to_owned();
771 while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
772 value.push(' ');
773 value.push_str(&ident);
774 }
775
776 if is_reserved_kw(&value) {
777 return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
778 }
779
780 Ok(SingleFontFamily::FamilyName(FamilyName {
781 name: Atom::from(&*value),
782 syntax: canonical_syntax_for(&value),
783 }))
784 }
785}
786
787#[derive(
789 Clone,
790 Debug,
791 Deserialize,
792 Hash,
793 Serialize,
794 ToComputedValue,
795 ToResolvedValue,
796 ToShmem,
797 PartialEq,
798 Eq,
799)]
800#[repr(C)]
801pub struct FontFamilyList {
802 pub list: crate::ArcSlice<SingleFontFamily>,
804}
805
806impl FontFamilyList {
807 pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
809 self.list.iter()
810 }
811
812 #[cfg_attr(feature = "servo", allow(unused))]
819 pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
820 let mut index_of_first_generic = None;
821 let mut target_index = None;
822
823 for (i, f) in self.iter().enumerate() {
824 match f {
825 SingleFontFamily::Generic(f) => {
826 if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
827 if target_index.is_none() {
831 return;
832 }
833 index_of_first_generic = Some(i);
834 break;
835 }
836 if target_index.is_none() {
839 target_index = Some(i);
840 }
841 },
842 SingleFontFamily::FamilyName(fam) => {
843 if target_index.is_none() && !fam.is_known_icon_font_family() {
846 target_index = Some(i);
847 }
848 },
849 }
850 }
851
852 let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
853 let first_generic = match index_of_first_generic {
854 Some(i) => new_list.remove(i),
855 None => SingleFontFamily::Generic(generic),
856 };
857
858 if let Some(i) = target_index {
859 new_list.insert(i, first_generic);
860 } else {
861 new_list.push(first_generic);
862 }
863 self.list = crate::ArcSlice::from_iter(new_list.into_iter());
864 }
865
866 #[cfg_attr(feature = "servo", allow(unused))]
868 pub(crate) fn needs_user_font_prioritization(&self) -> bool {
869 self.iter().next().is_none_or(|f| match f {
870 SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
871 _ => true,
872 })
873 }
874
875 pub fn single_generic(&self) -> Option<GenericFontFamily> {
877 let mut iter = self.iter();
878 if let Some(SingleFontFamily::Generic(f)) = iter.next()
879 && iter.next().is_none()
880 {
881 return Some(*f);
882 }
883 None
884 }
885}
886
887pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
889
890impl FontSizeAdjust {
891 #[inline]
892 pub fn none() -> Self {
894 FontSizeAdjust::None
895 }
896}
897
898impl ToComputedValue for specified::FontSizeAdjust {
899 type ComputedValue = FontSizeAdjust;
900
901 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
902 use crate::font_metrics::FontMetricsOrientation;
903
904 let font_metrics = |vertical, flags| {
905 let orient = if vertical {
906 FontMetricsOrientation::MatchContextPreferVertical
907 } else {
908 FontMetricsOrientation::Horizontal
909 };
910 let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
911 let font_size = context
912 .style()
913 .get_font()
914 .slow_clone_font_size()
915 .used_size
916 .0;
917 (metrics, font_size)
918 };
919
920 macro_rules! resolve {
924 ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
925 match $value {
926 specified::FontSizeAdjustFactor::Number(f) => {
927 FontSizeAdjust::$basis(f.to_computed_value(context))
928 },
929 specified::FontSizeAdjustFactor::FromFont => {
930 let (metrics, font_size) = font_metrics($vertical, $flags);
931 let ratio = if let Some(metric) = metrics.$field {
932 metric / font_size
933 } else if $fallback >= 0.0 {
934 $fallback
935 } else {
936 metrics.ascent / font_size
937 };
938 if ratio.is_nan() {
939 FontSizeAdjust::$basis(NonNegative(abs($fallback)))
940 } else {
941 FontSizeAdjust::$basis(NonNegative(ratio))
942 }
943 },
944 }
945 }};
946 }
947
948 match self {
949 Self::None => FontSizeAdjust::None,
950 Self::ExHeight(val) => {
951 resolve!(
952 ExHeight,
953 val,
954 false,
955 x_height,
956 0.5,
957 QueryFontMetricsFlags::empty()
958 )
959 },
960 Self::CapHeight(val) => {
961 resolve!(
962 CapHeight,
963 val,
964 false,
965 cap_height,
966 -1.0, QueryFontMetricsFlags::empty()
968 )
969 },
970 Self::ChWidth(val) => {
971 resolve!(
972 ChWidth,
973 val,
974 false,
975 zero_advance_measure,
976 0.5,
977 QueryFontMetricsFlags::NEEDS_CH
978 )
979 },
980 Self::IcWidth(val) => {
981 resolve!(
982 IcWidth,
983 val,
984 false,
985 ic_width,
986 1.0,
987 QueryFontMetricsFlags::NEEDS_IC
988 )
989 },
990 Self::IcHeight(val) => {
991 resolve!(
992 IcHeight,
993 val,
994 true,
995 ic_width,
996 1.0,
997 QueryFontMetricsFlags::NEEDS_IC
998 )
999 },
1000 }
1001 }
1002
1003 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1004 macro_rules! case {
1005 ($basis:ident, $val:expr) => {
1006 Self::$basis(specified::FontSizeAdjustFactor::Number(
1007 ToComputedValue::from_computed_value($val),
1008 ))
1009 };
1010 }
1011 match *computed {
1012 FontSizeAdjust::None => Self::None,
1013 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
1014 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
1015 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
1016 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
1017 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
1018 }
1019 }
1020}
1021
1022pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1024
1025pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1027
1028fn dedup_font_settings<T>(settings_list: &mut ThinVec<T>)
1031where
1032 T: TaggedFontValue,
1033{
1034 if settings_list.len() > 1 {
1035 settings_list.sort_by_key(|k| k.tag().0);
1036 settings_list.dedup_by(|a, b| {
1039 if a.tag() == b.tag() {
1040 std::mem::swap(a, b);
1041 true
1042 } else {
1043 false
1044 }
1045 });
1046 }
1047}
1048
1049impl<T> ToComputedValue for FontSettings<T>
1050where
1051 T: ToComputedValue,
1052 <T as ToComputedValue>::ComputedValue: TaggedFontValue,
1053{
1054 type ComputedValue = FontSettings<T::ComputedValue>;
1055
1056 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1057 let mut v = self
1058 .0
1059 .iter()
1060 .map(|item| item.to_computed_value(context))
1061 .collect::<ThinVec<_>>();
1062 dedup_font_settings(&mut v);
1063 FontSettings(v)
1064 }
1065
1066 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1067 Self(computed.0.iter().map(T::from_computed_value).collect())
1068 }
1069}
1070
1071#[derive(
1076 Clone,
1077 Copy,
1078 Debug,
1079 Deserialize,
1080 Eq,
1081 MallocSizeOf,
1082 PartialEq,
1083 Serialize,
1084 SpecifiedValueInfo,
1085 ToComputedValue,
1086 ToResolvedValue,
1087 ToShmem,
1088 ToTyped,
1089)]
1090#[repr(C)]
1091#[typed(todo_derive_fields)]
1092#[value_info(other_values = "normal")]
1093pub struct FontLanguageOverride(pub u32);
1094
1095impl FontLanguageOverride {
1096 #[inline]
1097 pub fn normal() -> FontLanguageOverride {
1099 FontLanguageOverride(0)
1100 }
1101
1102 #[inline]
1104 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
1105 *storage = u32::to_be_bytes(self.0);
1106 let slice = if cfg!(debug_assertions) {
1108 std::str::from_utf8(&storage[..]).unwrap()
1109 } else {
1110 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
1111 };
1112 slice.trim_end()
1113 }
1114
1115 #[inline]
1118 pub unsafe fn from_u32(value: u32) -> Self {
1119 Self(value)
1120 }
1121}
1122
1123impl ToCss for FontLanguageOverride {
1124 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1125 where
1126 W: fmt::Write,
1127 {
1128 if self.0 == 0 {
1129 return dest.write_str("normal");
1130 }
1131 self.to_str(&mut [0; 4]).to_css(dest)
1132 }
1133}
1134
1135impl ToComputedValue for specified::MozScriptMinSize {
1136 type ComputedValue = MozScriptMinSize;
1137
1138 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1139 let base_size = FontBaseSize::InheritedStyle;
1142 let line_height_base = LineHeightBase::InheritedStyle;
1143 self.0
1144 .to_computed_value_with_base_size(cx, base_size, line_height_base)
1145 }
1146
1147 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1148 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1149 }
1150}
1151
1152pub type MathDepth = i8;
1154
1155impl ToComputedValue for specified::MathDepth {
1156 type ComputedValue = MathDepth;
1157
1158 fn to_computed_value(&self, cx: &Context) -> i8 {
1159 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1160
1161 let int = match self {
1162 specified::MathDepth::AutoAdd => {
1163 let parent = *cx.builder.get_parent_font().get_math_depth() as i32;
1164 let style = *cx.builder.get_parent_font().get_math_style();
1165 if style == MathStyleValue::Compact {
1166 parent.saturating_add(1)
1167 } else {
1168 parent
1169 }
1170 },
1171 specified::MathDepth::Add(rel) => {
1172 let parent = *cx.builder.get_parent_font().get_math_depth();
1173 (parent as i32).saturating_add(rel.to_computed_value(cx))
1174 },
1175 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1176 };
1177 std::cmp::min(int, i8::MAX as i32) as i8
1178 }
1179
1180 fn from_computed_value(other: &i8) -> Self {
1181 let computed_value = *other as i32;
1182 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1183 }
1184}
1185
1186impl ToAnimatedValue for MathDepth {
1187 type AnimatedValue = CSSInteger;
1188
1189 #[inline]
1190 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1191 self.into()
1192 }
1193
1194 #[inline]
1195 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1196 std::cmp::min(animated, i8::MAX as i32) as i8
1197 }
1198}
1199
1200pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1205
1206pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1209
1210#[derive(
1221 Clone,
1222 ComputeSquaredDistance,
1223 Copy,
1224 Debug,
1225 Deserialize,
1226 Eq,
1227 Hash,
1228 MallocSizeOf,
1229 PartialEq,
1230 PartialOrd,
1231 Serialize,
1232 ToResolvedValue,
1233 ToTyped,
1234)]
1235#[repr(C)]
1236#[typed(todo_derive_fields)]
1237pub struct FontStyle(FontStyleFixedPoint);
1238
1239impl FontStyle {
1240 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1242 value: 0 << FONT_STYLE_FRACTION_BITS,
1243 });
1244
1245 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1247 value: 100 << FONT_STYLE_FRACTION_BITS,
1248 });
1249
1250 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1253
1254 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1256 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1257 });
1258
1259 #[inline]
1261 pub fn normal() -> Self {
1262 Self::NORMAL
1263 }
1264
1265 pub fn oblique(degrees: f32) -> Self {
1267 Self(FixedPoint::from_float(
1268 degrees
1269 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1270 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1271 ))
1272 }
1273
1274 pub fn oblique_degrees(&self) -> f32 {
1276 debug_assert_ne!(*self, Self::ITALIC);
1277 self.0.to_float()
1278 }
1279}
1280
1281impl ToCss for FontStyle {
1282 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1283 where
1284 W: fmt::Write,
1285 {
1286 if *self == Self::NORMAL {
1287 return dest.write_str("normal");
1288 }
1289 if *self == Self::ITALIC {
1290 return dest.write_str("italic");
1291 }
1292 dest.write_str("oblique")?;
1293 if *self != Self::OBLIQUE {
1294 dest.write_char(' ')?;
1296 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1297 }
1298 Ok(())
1299 }
1300}
1301
1302impl ToAnimatedValue for FontStyle {
1303 type AnimatedValue = generics::FontStyle<Angle>;
1304
1305 #[inline]
1306 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1307 if self == Self::ITALIC {
1308 return generics::FontStyle::Italic;
1309 }
1310 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1311 }
1312
1313 #[inline]
1314 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1315 match animated {
1316 generics::FontStyle::Italic => Self::ITALIC,
1317 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1318 }
1319 }
1320}
1321
1322pub const FONT_WIDTH_FRACTION_BITS: u16 = 6;
1329
1330pub type FontWidthFixedPoint = FixedPoint<u16, FONT_WIDTH_FRACTION_BITS>;
1333
1334#[derive(
1345 Clone,
1346 ComputeSquaredDistance,
1347 Copy,
1348 Debug,
1349 Deserialize,
1350 Hash,
1351 MallocSizeOf,
1352 PartialEq,
1353 PartialOrd,
1354 Serialize,
1355 ToResolvedValue,
1356)]
1357#[repr(C)]
1358pub struct FontWidth(pub FontWidthFixedPoint);
1359
1360impl FontWidth {
1361 pub const FRACTION_BITS: u16 = FONT_WIDTH_FRACTION_BITS;
1363 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
1365
1366 pub const ULTRA_CONDENSED: FontWidth = FontWidth(FontWidthFixedPoint {
1368 value: 50 << Self::FRACTION_BITS,
1369 });
1370 pub const EXTRA_CONDENSED: FontWidth = FontWidth(FontWidthFixedPoint {
1372 value: (62 << Self::FRACTION_BITS) + Self::HALF,
1373 });
1374 pub const CONDENSED: FontWidth = FontWidth(FontWidthFixedPoint {
1376 value: 75 << Self::FRACTION_BITS,
1377 });
1378 pub const SEMI_CONDENSED: FontWidth = FontWidth(FontWidthFixedPoint {
1380 value: (87 << Self::FRACTION_BITS) + Self::HALF,
1381 });
1382 pub const NORMAL: FontWidth = FontWidth(FontWidthFixedPoint {
1384 value: 100 << Self::FRACTION_BITS,
1385 });
1386 pub const SEMI_EXPANDED: FontWidth = FontWidth(FontWidthFixedPoint {
1388 value: (112 << Self::FRACTION_BITS) + Self::HALF,
1389 });
1390 pub const EXPANDED: FontWidth = FontWidth(FontWidthFixedPoint {
1392 value: 125 << Self::FRACTION_BITS,
1393 });
1394 pub const EXTRA_EXPANDED: FontWidth = FontWidth(FontWidthFixedPoint {
1396 value: 150 << Self::FRACTION_BITS,
1397 });
1398 pub const ULTRA_EXPANDED: FontWidth = FontWidth(FontWidthFixedPoint {
1400 value: 200 << Self::FRACTION_BITS,
1401 });
1402
1403 pub fn hundred() -> Self {
1405 Self::NORMAL
1406 }
1407
1408 #[inline]
1410 pub fn to_percentage(&self) -> Percentage {
1411 Percentage(self.0.to_float() / 100.0)
1412 }
1413
1414 pub fn from_percentage(p: f32) -> Self {
1416 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
1417 }
1418
1419 pub fn from_keyword(kw: specified::FontWidthKeyword) -> Self {
1422 use specified::FontWidthKeyword::*;
1423 match kw {
1424 UltraCondensed => Self::ULTRA_CONDENSED,
1425 ExtraCondensed => Self::EXTRA_CONDENSED,
1426 Condensed => Self::CONDENSED,
1427 SemiCondensed => Self::SEMI_CONDENSED,
1428 Normal => Self::NORMAL,
1429 SemiExpanded => Self::SEMI_EXPANDED,
1430 Expanded => Self::EXPANDED,
1431 ExtraExpanded => Self::EXTRA_EXPANDED,
1432 UltraExpanded => Self::ULTRA_EXPANDED,
1433 }
1434 }
1435
1436 pub fn as_keyword(&self) -> Option<specified::FontWidthKeyword> {
1438 use specified::FontWidthKeyword::*;
1439 if *self == Self::ULTRA_CONDENSED {
1441 return Some(UltraCondensed);
1442 }
1443 if *self == Self::EXTRA_CONDENSED {
1444 return Some(ExtraCondensed);
1445 }
1446 if *self == Self::CONDENSED {
1447 return Some(Condensed);
1448 }
1449 if *self == Self::SEMI_CONDENSED {
1450 return Some(SemiCondensed);
1451 }
1452 if *self == Self::NORMAL {
1453 return Some(Normal);
1454 }
1455 if *self == Self::SEMI_EXPANDED {
1456 return Some(SemiExpanded);
1457 }
1458 if *self == Self::EXPANDED {
1459 return Some(Expanded);
1460 }
1461 if *self == Self::EXTRA_EXPANDED {
1462 return Some(ExtraExpanded);
1463 }
1464 if *self == Self::ULTRA_EXPANDED {
1465 return Some(UltraExpanded);
1466 }
1467 None
1468 }
1469}
1470
1471impl ToCss for FontWidth {
1472 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1473 where
1474 W: fmt::Write,
1475 {
1476 self.to_percentage().to_css(dest)
1477 }
1478}
1479
1480impl ToTyped for FontWidth {
1481 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1482 match self.as_keyword() {
1483 Some(keyword) => keyword.to_typed(dest),
1484 None => self.to_percentage().to_typed(dest),
1485 }
1486 }
1487}
1488
1489impl ToAnimatedValue for FontWidth {
1490 type AnimatedValue = Percentage;
1491
1492 #[inline]
1493 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1494 self.to_percentage()
1495 }
1496
1497 #[inline]
1498 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1499 Self::from_percentage(animated.0)
1500 }
1501}
1502
1503pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1505
1506impl ToResolvedValue for LineHeight {
1507 type ResolvedValue = Self;
1508
1509 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1510 #[cfg(feature = "gecko")]
1511 {
1512 if matches!(self, Self::Normal) {
1514 return self;
1515 }
1516 let wm = context.style.writing_mode;
1517 Self::Length(
1518 context
1519 .device
1520 .calc_line_height(
1521 context.style.get_font(),
1522 wm,
1523 Some(context.element_info.element),
1524 )
1525 .to_resolved_value(context),
1526 )
1527 }
1528 #[cfg(feature = "servo")]
1529 {
1530 if let LineHeight::Number(num) = &self {
1531 let size = context
1532 .style
1533 .get_font()
1534 .slow_clone_font_size()
1535 .computed_size();
1536 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1537 } else {
1538 self
1539 }
1540 }
1541 }
1542
1543 #[inline]
1544 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1545 value
1546 }
1547}