1use crate::derives::*;
8use crate::parser::{Parse, ParserContext};
9use crate::values::animated::ToAnimatedValue;
10use crate::values::computed::{
11 Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,
12 ToComputedValue, Zoom,
13};
14use crate::values::generics::font::{
15 FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,
16};
17use crate::values::generics::{font as generics, NonNegative};
18use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
19use crate::values::specified::font::{
20 self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
21};
22use crate::values::specified::length::{FontBaseSize, LineHeightBase, NoCalcLength};
23use crate::values::CSSInteger;
24use crate::Atom;
25use cssparser::{match_ignore_ascii_case, serialize_identifier, CssStringWriter, Parser};
26use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
27use num_traits::abs;
28use num_traits::cast::AsPrimitive;
29use std::fmt::{self, Write};
30use style_traits::{CssWriter, ParseError, ToCss, ToTyped, TypedValue};
31use thin_vec::ThinVec;
32
33pub use crate::values::computed::Length as MozScriptMinSize;
34pub use crate::values::specified::font::MozScriptSizeMultiplier;
35pub use crate::values::specified::font::{FontPalette, FontSynthesis, FontSynthesisStyle};
36pub use crate::values::specified::font::{
37 FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric,
38 QueryFontMetricsFlags, XLang, XTextScale,
39};
40pub use crate::values::specified::Integer as SpecifiedInteger;
41pub use crate::values::specified::Number as SpecifiedNumber;
42
43#[repr(C)]
62#[derive(
63 Clone,
64 ComputeSquaredDistance,
65 Copy,
66 Debug,
67 Eq,
68 Hash,
69 MallocSizeOf,
70 PartialEq,
71 PartialOrd,
72 ToResolvedValue,
73)]
74#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
75pub struct FixedPoint<T, const FRACTION_BITS: u16> {
76 pub value: T,
78}
79
80impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
81where
82 T: AsPrimitive<f32>,
83 f32: AsPrimitive<T>,
84 u16: AsPrimitive<T>,
85{
86 const SCALE: u16 = 1 << FRACTION_BITS;
87 const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;
88
89 pub fn from_float(v: f32) -> Self {
91 Self {
92 value: (v * Self::SCALE as f32).round().as_(),
93 }
94 }
95
96 pub fn to_float(&self) -> f32 {
98 self.value.as_() * Self::INVERSE_SCALE
99 }
100}
101
102impl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> {
105 type Output = Self;
106 fn div(self, rhs: Self) -> Self {
107 Self {
108 value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16,
109 }
110 }
111}
112impl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> {
113 type Output = Self;
114 fn mul(self, rhs: Self) -> Self {
115 Self {
116 value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16,
117 }
118 }
119}
120
121pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
126
127pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;
130
131#[derive(
140 Clone,
141 ComputeSquaredDistance,
142 Copy,
143 Debug,
144 Hash,
145 MallocSizeOf,
146 PartialEq,
147 PartialOrd,
148 ToResolvedValue,
149)]
150#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
151#[repr(C)]
152pub struct FontWeight(FontWeightFixedPoint);
153impl ToAnimatedValue for FontWeight {
154 type AnimatedValue = Number;
155
156 #[inline]
157 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
158 self.value()
159 }
160
161 #[inline]
162 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
163 FontWeight::from_float(animated)
164 }
165}
166
167impl ToCss for FontWeight {
168 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
169 where
170 W: fmt::Write,
171 {
172 self.value().to_css(dest)
173 }
174}
175
176impl ToTyped for FontWeight {
177 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
178 self.value().to_typed(dest)
179 }
180}
181
182impl FontWeight {
183 pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
185 value: 400 << FONT_WEIGHT_FRACTION_BITS,
186 });
187
188 pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
190 value: 700 << FONT_WEIGHT_FRACTION_BITS,
191 });
192
193 pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
195 value: 600 << FONT_WEIGHT_FRACTION_BITS,
196 });
197
198 pub const PREFER_BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
201 value: 500 << FONT_WEIGHT_FRACTION_BITS,
202 });
203
204 pub fn normal() -> Self {
206 Self::NORMAL
207 }
208
209 pub fn is_bold(&self) -> bool {
211 *self >= Self::BOLD_THRESHOLD
212 }
213
214 pub fn value(&self) -> f32 {
216 self.0.to_float()
217 }
218
219 pub fn from_float(v: f32) -> Self {
221 Self(FixedPoint::from_float(
222 v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
223 ))
224 }
225
226 pub fn bolder(self) -> Self {
231 let value = self.value();
232 if value < 350. {
233 return Self::NORMAL;
234 }
235 if value < 550. {
236 return Self::BOLD;
237 }
238 Self::from_float(value.max(900.))
239 }
240
241 pub fn lighter(self) -> Self {
246 let value = self.value();
247 if value < 550. {
248 return Self::from_float(value.min(100.));
249 }
250 if value < 750. {
251 return Self::NORMAL;
252 }
253 Self::BOLD
254 }
255}
256
257#[derive(
258 Animate,
259 Clone,
260 ComputeSquaredDistance,
261 Copy,
262 Debug,
263 MallocSizeOf,
264 PartialEq,
265 ToAnimatedZero,
266 ToCss,
267 ToTyped,
268)]
269#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
270pub struct FontSize {
272 pub computed_size: NonNegativeLength,
275 #[css(skip)]
278 pub used_size: NonNegativeLength,
279 #[css(skip)]
281 pub keyword_info: KeywordInfo,
282}
283
284impl FontSize {
285 #[inline]
287 pub fn computed_size(&self) -> Length {
288 self.computed_size.0
289 }
290
291 #[inline]
293 pub fn used_size(&self) -> Length {
294 self.used_size.0
295 }
296
297 #[inline]
299 pub fn zoom(&self, zoom: Zoom) -> Self {
300 Self {
301 computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
302 used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
303 keyword_info: self.keyword_info,
304 }
305 }
306
307 #[inline]
308 pub fn medium() -> Self {
310 Self {
311 computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
312 used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
313 keyword_info: KeywordInfo::medium(),
314 }
315 }
316}
317
318impl ToAnimatedValue for FontSize {
319 type AnimatedValue = Length;
320
321 #[inline]
322 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
323 self.computed_size.0.to_animated_value(context)
324 }
325
326 #[inline]
327 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
328 FontSize {
329 computed_size: NonNegative(animated.clamp_to_non_negative()),
330 used_size: NonNegative(animated.clamp_to_non_negative()),
331 keyword_info: KeywordInfo::none(),
332 }
333 }
334}
335
336impl ToResolvedValue for FontSize {
337 type ResolvedValue = NonNegativeLength;
338
339 #[inline]
340 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
341 self.computed_size.to_resolved_value(context)
342 }
343
344 #[inline]
345 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
346 let computed_size = NonNegativeLength::from_resolved_value(resolved);
347 Self {
348 computed_size,
349 used_size: computed_size,
350 keyword_info: KeywordInfo::none(),
351 }
352 }
353}
354
355#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue, ToTyped)]
356#[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))]
357#[repr(C)]
359#[typed(todo_derive_fields)]
360pub struct FontFamily {
361 pub families: FontFamilyList,
363 pub is_system_font: bool,
365 pub is_initial: bool,
368}
369
370macro_rules! static_font_family {
371 ($ident:ident, $family:expr) => {
372 static $ident: std::sync::LazyLock<FontFamily> = std::sync::LazyLock::new(|| FontFamily {
373 families: FontFamilyList {
374 list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
375 },
376 is_system_font: false,
377 is_initial: false,
378 });
379 };
380}
381
382impl FontFamily {
383 #[inline]
384 pub fn serif() -> Self {
386 Self::generic(GenericFontFamily::Serif).clone()
387 }
388
389 #[cfg(feature = "gecko")]
391 pub(crate) fn moz_bullet() -> &'static Self {
392 static_font_family!(
393 MOZ_BULLET,
394 SingleFontFamily::FamilyName(FamilyName {
395 name: atom!("-moz-bullet-font"),
396 syntax: FontFamilyNameSyntax::Identifiers,
397 })
398 );
399
400 &*MOZ_BULLET
401 }
402
403 #[cfg(feature = "gecko")]
405 pub fn for_system_font(name: &str) -> Self {
406 Self {
407 families: FontFamilyList {
408 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
409 FamilyName {
410 name: Atom::from(name),
411 syntax: FontFamilyNameSyntax::Identifiers,
412 },
413 ))),
414 },
415 is_system_font: true,
416 is_initial: false,
417 }
418 }
419
420 pub fn generic(generic: GenericFontFamily) -> &'static Self {
422 macro_rules! generic_font_family {
423 ($ident:ident, $family:ident) => {
424 static_font_family!(
425 $ident,
426 SingleFontFamily::Generic(GenericFontFamily::$family)
427 )
428 };
429 }
430
431 generic_font_family!(SERIF, Serif);
432 generic_font_family!(SANS_SERIF, SansSerif);
433 generic_font_family!(MONOSPACE, Monospace);
434 generic_font_family!(CURSIVE, Cursive);
435 generic_font_family!(FANTASY, Fantasy);
436 #[cfg(feature = "gecko")]
437 generic_font_family!(MATH, Math);
438 #[cfg(feature = "gecko")]
439 generic_font_family!(MOZ_EMOJI, MozEmoji);
440 generic_font_family!(SYSTEM_UI, SystemUi);
441
442 let family = match generic {
443 GenericFontFamily::None => {
444 debug_assert!(false, "Bogus caller!");
445 &*SERIF
446 },
447 GenericFontFamily::Serif => &*SERIF,
448 GenericFontFamily::SansSerif => &*SANS_SERIF,
449 GenericFontFamily::Monospace => &*MONOSPACE,
450 GenericFontFamily::Cursive => &*CURSIVE,
451 GenericFontFamily::Fantasy => &*FANTASY,
452 #[cfg(feature = "gecko")]
453 GenericFontFamily::Math => &*MATH,
454 #[cfg(feature = "gecko")]
455 GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
456 GenericFontFamily::SystemUi => &*SYSTEM_UI,
457 };
458 debug_assert_eq!(
459 *family.families.iter().next().unwrap(),
460 SingleFontFamily::Generic(generic)
461 );
462 family
463 }
464}
465
466impl MallocSizeOf for FontFamily {
467 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
468 use malloc_size_of::MallocUnconditionalSizeOf;
469 let shared_font_list = &self.families.list;
473 if shared_font_list.is_unique() {
474 shared_font_list.unconditional_size_of(ops)
475 } else {
476 0
477 }
478 }
479}
480
481impl ToCss for FontFamily {
482 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
483 where
484 W: fmt::Write,
485 {
486 let mut iter = self.families.iter();
487 match iter.next() {
488 Some(f) => f.to_css(dest)?,
489 None => return Ok(()),
490 }
491 for family in iter {
492 dest.write_str(", ")?;
493 family.to_css(dest)?;
494 }
495 Ok(())
496 }
497}
498
499#[derive(
501 Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
502)]
503#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
504#[repr(C)]
505pub struct FamilyName {
506 pub name: Atom,
508 pub syntax: FontFamilyNameSyntax,
510}
511
512#[cfg(feature = "gecko")]
513impl FamilyName {
514 fn is_known_icon_font_family(&self) -> bool {
515 use crate::gecko_bindings::bindings;
516 unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
517 }
518}
519
520#[cfg(feature = "servo")]
521impl FamilyName {
522 fn is_known_icon_font_family(&self) -> bool {
523 false
524 }
525}
526
527impl ToCss for FamilyName {
528 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
529 where
530 W: fmt::Write,
531 {
532 match self.syntax {
533 FontFamilyNameSyntax::Quoted => {
534 dest.write_char('"')?;
535 write!(CssStringWriter::new(dest), "{}", self.name)?;
536 dest.write_char('"')
537 },
538 FontFamilyNameSyntax::Identifiers => {
539 let mut first = true;
540 for ident in self.name.to_string().split(' ') {
541 if first {
542 first = false;
543 } else {
544 dest.write_char(' ')?;
545 }
546 debug_assert!(
547 !ident.is_empty(),
548 "Family name with leading, \
549 trailing, or consecutive white spaces should \
550 have been marked quoted by the parser"
551 );
552 serialize_identifier(ident, dest)?;
553 }
554 Ok(())
555 },
556 }
557 }
558}
559
560#[derive(
561 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
562)]
563#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
564#[repr(u8)]
567pub enum FontFamilyNameSyntax {
568 Quoted,
571
572 Identifiers,
575}
576
577#[derive(
580 Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
581)]
582#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
583#[repr(u8)]
584pub enum SingleFontFamily {
585 FamilyName(FamilyName),
587 Generic(GenericFontFamily),
589}
590
591fn system_ui_enabled(_: &ParserContext) -> bool {
592 static_prefs::pref!("layout.css.system-ui.enabled")
593}
594
595#[cfg(feature = "gecko")]
596fn math_enabled(context: &ParserContext) -> bool {
597 context.chrome_rules_enabled() || static_prefs::pref!("mathml.font_family_math.enabled")
598}
599
600#[derive(
610 Clone,
611 Copy,
612 Debug,
613 Eq,
614 Hash,
615 MallocSizeOf,
616 PartialEq,
617 Parse,
618 ToCss,
619 ToComputedValue,
620 ToResolvedValue,
621 ToShmem,
622)]
623#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
624#[repr(u32)]
625#[allow(missing_docs)]
626pub enum GenericFontFamily {
627 #[css(skip)]
631 None = 0,
632 Serif,
633 SansSerif,
634 #[parse(aliases = "-moz-fixed")]
635 Monospace,
636 Cursive,
637 Fantasy,
638 #[cfg(feature = "gecko")]
639 #[parse(condition = "math_enabled")]
640 Math,
641 #[parse(condition = "system_ui_enabled")]
642 SystemUi,
643 #[css(skip)]
645 #[cfg(feature = "gecko")]
646 MozEmoji,
647}
648
649impl GenericFontFamily {
650 pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
654 match self {
655 Self::None | Self::Cursive | Self::Fantasy | Self::SystemUi => false,
656 #[cfg(feature = "gecko")]
657 Self::Math | Self::MozEmoji => false,
658 Self::Serif | Self::SansSerif | Self::Monospace => true,
659 }
660 }
661}
662
663impl Parse for SingleFontFamily {
664 fn parse<'i, 't>(
666 context: &ParserContext,
667 input: &mut Parser<'i, 't>,
668 ) -> Result<Self, ParseError<'i>> {
669 if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
670 return Ok(SingleFontFamily::FamilyName(FamilyName {
671 name: Atom::from(&*value),
672 syntax: FontFamilyNameSyntax::Quoted,
673 }));
674 }
675
676 if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
677 return Ok(SingleFontFamily::Generic(generic));
678 }
679
680 let first_ident = input.expect_ident_cloned()?;
681 let reserved = match_ignore_ascii_case! { &first_ident,
682 "inherit" | "initial" | "unset" | "revert" | "default" => true,
690 _ => false,
691 };
692
693 let mut value = first_ident.as_ref().to_owned();
694 let mut serialize_quoted = value.contains(' ');
695
696 if reserved {
699 let ident = input.expect_ident()?;
700 serialize_quoted = serialize_quoted || ident.contains(' ');
701 value.push(' ');
702 value.push_str(&ident);
703 }
704 while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
705 serialize_quoted = serialize_quoted || ident.contains(' ');
706 value.push(' ');
707 value.push_str(&ident);
708 }
709 let syntax = if serialize_quoted {
710 FontFamilyNameSyntax::Quoted
715 } else {
716 FontFamilyNameSyntax::Identifiers
717 };
718 Ok(SingleFontFamily::FamilyName(FamilyName {
719 name: Atom::from(value),
720 syntax,
721 }))
722 }
723}
724
725#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
727#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
728#[repr(C)]
729pub struct FontFamilyList {
730 pub list: crate::ArcSlice<SingleFontFamily>,
732}
733
734impl FontFamilyList {
735 pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
737 self.list.iter()
738 }
739
740 #[cfg_attr(feature = "servo", allow(unused))]
747 pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
748 let mut index_of_first_generic = None;
749 let mut target_index = None;
750
751 for (i, f) in self.iter().enumerate() {
752 match &*f {
753 SingleFontFamily::Generic(f) => {
754 if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
755 if target_index.is_none() {
759 return;
760 }
761 index_of_first_generic = Some(i);
762 break;
763 }
764 if target_index.is_none() {
767 target_index = Some(i);
768 }
769 },
770 SingleFontFamily::FamilyName(fam) => {
771 if target_index.is_none() && !fam.is_known_icon_font_family() {
774 target_index = Some(i);
775 }
776 },
777 }
778 }
779
780 let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
781 let first_generic = match index_of_first_generic {
782 Some(i) => new_list.remove(i),
783 None => SingleFontFamily::Generic(generic),
784 };
785
786 if let Some(i) = target_index {
787 new_list.insert(i, first_generic);
788 } else {
789 new_list.push(first_generic);
790 }
791 self.list = crate::ArcSlice::from_iter(new_list.into_iter());
792 }
793
794 #[cfg_attr(feature = "servo", allow(unused))]
796 pub(crate) fn needs_user_font_prioritization(&self) -> bool {
797 self.iter().next().map_or(true, |f| match f {
798 SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
799 _ => true,
800 })
801 }
802
803 pub fn single_generic(&self) -> Option<GenericFontFamily> {
805 let mut iter = self.iter();
806 if let Some(SingleFontFamily::Generic(f)) = iter.next() {
807 if iter.next().is_none() {
808 return Some(*f);
809 }
810 }
811 None
812 }
813}
814
815pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
817
818impl FontSizeAdjust {
819 #[inline]
820 pub fn none() -> Self {
822 FontSizeAdjust::None
823 }
824}
825
826impl ToComputedValue for specified::FontSizeAdjust {
827 type ComputedValue = FontSizeAdjust;
828
829 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
830 use crate::font_metrics::FontMetricsOrientation;
831
832 let font_metrics = |vertical, flags| {
833 let orient = if vertical {
834 FontMetricsOrientation::MatchContextPreferVertical
835 } else {
836 FontMetricsOrientation::Horizontal
837 };
838 let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
839 let font_size = context.style().get_font().clone_font_size().used_size.0;
840 (metrics, font_size)
841 };
842
843 macro_rules! resolve {
847 ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
848 match $value {
849 specified::FontSizeAdjustFactor::Number(f) => {
850 FontSizeAdjust::$basis(f.to_computed_value(context))
851 },
852 specified::FontSizeAdjustFactor::FromFont => {
853 let (metrics, font_size) = font_metrics($vertical, $flags);
854 let ratio = if let Some(metric) = metrics.$field {
855 metric / font_size
856 } else if $fallback >= 0.0 {
857 $fallback
858 } else {
859 metrics.ascent / font_size
860 };
861 if ratio.is_nan() {
862 FontSizeAdjust::$basis(NonNegative(abs($fallback)))
863 } else {
864 FontSizeAdjust::$basis(NonNegative(ratio))
865 }
866 },
867 }
868 }};
869 }
870
871 match *self {
872 Self::None => FontSizeAdjust::None,
873 Self::ExHeight(val) => {
874 resolve!(
875 ExHeight,
876 val,
877 false,
878 x_height,
879 0.5,
880 QueryFontMetricsFlags::empty()
881 )
882 },
883 Self::CapHeight(val) => {
884 resolve!(
885 CapHeight,
886 val,
887 false,
888 cap_height,
889 -1.0, QueryFontMetricsFlags::empty()
891 )
892 },
893 Self::ChWidth(val) => {
894 resolve!(
895 ChWidth,
896 val,
897 false,
898 zero_advance_measure,
899 0.5,
900 QueryFontMetricsFlags::NEEDS_CH
901 )
902 },
903 Self::IcWidth(val) => {
904 resolve!(
905 IcWidth,
906 val,
907 false,
908 ic_width,
909 1.0,
910 QueryFontMetricsFlags::NEEDS_IC
911 )
912 },
913 Self::IcHeight(val) => {
914 resolve!(
915 IcHeight,
916 val,
917 true,
918 ic_width,
919 1.0,
920 QueryFontMetricsFlags::NEEDS_IC
921 )
922 },
923 }
924 }
925
926 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
927 macro_rules! case {
928 ($basis:ident, $val:expr) => {
929 Self::$basis(specified::FontSizeAdjustFactor::Number(
930 ToComputedValue::from_computed_value($val),
931 ))
932 };
933 }
934 match *computed {
935 FontSizeAdjust::None => Self::None,
936 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
937 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
938 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
939 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
940 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
941 }
942 }
943}
944
945pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
947
948pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
950
951fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
954where
955 T: TaggedFontValue,
956{
957 if settings_list.len() > 1 {
958 settings_list.sort_by_key(|k| k.tag().0);
959 let mut prev_tag = settings_list.last().unwrap().tag();
962 for i in (0..settings_list.len() - 1).rev() {
963 let cur_tag = settings_list[i].tag();
964 if cur_tag == prev_tag {
965 settings_list.remove(i);
966 }
967 prev_tag = cur_tag;
968 }
969 }
970}
971
972impl<T> ToComputedValue for FontSettings<T>
973where
974 T: ToComputedValue,
975 <T as ToComputedValue>::ComputedValue: TaggedFontValue,
976{
977 type ComputedValue = FontSettings<T::ComputedValue>;
978
979 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
980 let mut v = self
981 .0
982 .iter()
983 .map(|item| item.to_computed_value(context))
984 .collect::<Vec<_>>();
985 dedup_font_settings(&mut v);
986 FontSettings(v.into_boxed_slice())
987 }
988
989 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
990 Self(computed.0.iter().map(T::from_computed_value).collect())
991 }
992}
993
994#[derive(
999 Clone,
1000 Copy,
1001 Debug,
1002 Eq,
1003 MallocSizeOf,
1004 PartialEq,
1005 SpecifiedValueInfo,
1006 ToComputedValue,
1007 ToResolvedValue,
1008 ToShmem,
1009 ToTyped,
1010)]
1011#[repr(C)]
1012#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1013#[typed(todo_derive_fields)]
1014#[value_info(other_values = "normal")]
1015pub struct FontLanguageOverride(pub u32);
1016
1017impl FontLanguageOverride {
1018 #[inline]
1019 pub fn normal() -> FontLanguageOverride {
1021 FontLanguageOverride(0)
1022 }
1023
1024 #[inline]
1026 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
1027 *storage = u32::to_be_bytes(self.0);
1028 let slice = if cfg!(debug_assertions) {
1030 std::str::from_utf8(&storage[..]).unwrap()
1031 } else {
1032 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
1033 };
1034 slice.trim_end()
1035 }
1036
1037 #[inline]
1040 pub unsafe fn from_u32(value: u32) -> Self {
1041 Self(value)
1042 }
1043}
1044
1045impl ToCss for FontLanguageOverride {
1046 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1047 where
1048 W: fmt::Write,
1049 {
1050 if self.0 == 0 {
1051 return dest.write_str("normal");
1052 }
1053 self.to_str(&mut [0; 4]).to_css(dest)
1054 }
1055}
1056
1057impl ToComputedValue for specified::MozScriptMinSize {
1058 type ComputedValue = MozScriptMinSize;
1059
1060 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1061 let base_size = FontBaseSize::InheritedStyle;
1064 let line_height_base = LineHeightBase::InheritedStyle;
1065 match self.0 {
1066 NoCalcLength::FontRelative(value) => {
1067 value.to_computed_value(cx, base_size, line_height_base)
1068 },
1069 NoCalcLength::ServoCharacterWidth(value) => {
1070 value.to_computed_value(base_size.resolve(cx).computed_size())
1071 },
1072 ref l => l.to_computed_value(cx),
1073 }
1074 }
1075
1076 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1077 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1078 }
1079}
1080
1081pub type MathDepth = i8;
1083
1084#[cfg(feature = "gecko")]
1085impl ToComputedValue for specified::MathDepth {
1086 type ComputedValue = MathDepth;
1087
1088 fn to_computed_value(&self, cx: &Context) -> i8 {
1089 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1090 use std::{cmp, i8};
1091
1092 let int = match *self {
1093 specified::MathDepth::AutoAdd => {
1094 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
1095 let style = cx.builder.get_parent_font().clone_math_style();
1096 if style == MathStyleValue::Compact {
1097 parent.saturating_add(1)
1098 } else {
1099 parent
1100 }
1101 },
1102 specified::MathDepth::Add(rel) => {
1103 let parent = cx.builder.get_parent_font().clone_math_depth();
1104 (parent as i32).saturating_add(rel.to_computed_value(cx))
1105 },
1106 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1107 };
1108 cmp::min(int, i8::MAX as i32) as i8
1109 }
1110
1111 fn from_computed_value(other: &i8) -> Self {
1112 let computed_value = *other as i32;
1113 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1114 }
1115}
1116
1117impl ToAnimatedValue for MathDepth {
1118 type AnimatedValue = CSSInteger;
1119
1120 #[inline]
1121 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1122 self.into()
1123 }
1124
1125 #[inline]
1126 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1127 use std::{cmp, i8};
1128 cmp::min(animated, i8::MAX as i32) as i8
1129 }
1130}
1131
1132pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1137
1138pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1141
1142#[derive(
1153 Clone,
1154 ComputeSquaredDistance,
1155 Copy,
1156 Debug,
1157 Eq,
1158 Hash,
1159 MallocSizeOf,
1160 PartialEq,
1161 PartialOrd,
1162 ToResolvedValue,
1163 ToTyped,
1164)]
1165#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1166#[repr(C)]
1167#[typed(todo_derive_fields)]
1168pub struct FontStyle(FontStyleFixedPoint);
1169
1170impl FontStyle {
1171 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1173 value: 0 << FONT_STYLE_FRACTION_BITS,
1174 });
1175
1176 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1178 value: 100 << FONT_STYLE_FRACTION_BITS,
1179 });
1180
1181 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1184
1185 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1187 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1188 });
1189
1190 #[inline]
1192 pub fn normal() -> Self {
1193 Self::NORMAL
1194 }
1195
1196 pub fn oblique(degrees: f32) -> Self {
1198 Self(FixedPoint::from_float(
1199 degrees
1200 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1201 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1202 ))
1203 }
1204
1205 pub fn oblique_degrees(&self) -> f32 {
1207 debug_assert_ne!(*self, Self::ITALIC);
1208 self.0.to_float()
1209 }
1210}
1211
1212impl ToCss for FontStyle {
1213 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1214 where
1215 W: fmt::Write,
1216 {
1217 if *self == Self::NORMAL {
1218 return dest.write_str("normal");
1219 }
1220 if *self == Self::ITALIC {
1221 return dest.write_str("italic");
1222 }
1223 dest.write_str("oblique")?;
1224 if *self != Self::OBLIQUE {
1225 dest.write_char(' ')?;
1227 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1228 }
1229 Ok(())
1230 }
1231}
1232
1233impl ToAnimatedValue for FontStyle {
1234 type AnimatedValue = generics::FontStyle<Angle>;
1235
1236 #[inline]
1237 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1238 if self == Self::ITALIC {
1239 return generics::FontStyle::Italic;
1240 }
1241 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1242 }
1243
1244 #[inline]
1245 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1246 match animated {
1247 generics::FontStyle::Italic => Self::ITALIC,
1248 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1249 }
1250 }
1251}
1252
1253pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
1260
1261pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
1264
1265#[derive(
1274 Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,
1275)]
1276#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1277#[repr(C)]
1278pub struct FontStretch(pub FontStretchFixedPoint);
1279
1280impl FontStretch {
1281 pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
1283 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
1285
1286 pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1288 value: 50 << Self::FRACTION_BITS,
1289 });
1290 pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1292 value: (62 << Self::FRACTION_BITS) + Self::HALF,
1293 });
1294 pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1296 value: 75 << Self::FRACTION_BITS,
1297 });
1298 pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1300 value: (87 << Self::FRACTION_BITS) + Self::HALF,
1301 });
1302 pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
1304 value: 100 << Self::FRACTION_BITS,
1305 });
1306 pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1308 value: (112 << Self::FRACTION_BITS) + Self::HALF,
1309 });
1310 pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1312 value: 125 << Self::FRACTION_BITS,
1313 });
1314 pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1316 value: 150 << Self::FRACTION_BITS,
1317 });
1318 pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1320 value: 200 << Self::FRACTION_BITS,
1321 });
1322
1323 pub fn hundred() -> Self {
1325 Self::NORMAL
1326 }
1327
1328 #[inline]
1330 pub fn to_percentage(&self) -> Percentage {
1331 Percentage(self.0.to_float() / 100.0)
1332 }
1333
1334 pub fn from_percentage(p: f32) -> Self {
1336 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
1337 }
1338
1339 pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
1342 use specified::FontStretchKeyword::*;
1343 match kw {
1344 UltraCondensed => Self::ULTRA_CONDENSED,
1345 ExtraCondensed => Self::EXTRA_CONDENSED,
1346 Condensed => Self::CONDENSED,
1347 SemiCondensed => Self::SEMI_CONDENSED,
1348 Normal => Self::NORMAL,
1349 SemiExpanded => Self::SEMI_EXPANDED,
1350 Expanded => Self::EXPANDED,
1351 ExtraExpanded => Self::EXTRA_EXPANDED,
1352 UltraExpanded => Self::ULTRA_EXPANDED,
1353 }
1354 }
1355
1356 pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
1358 use specified::FontStretchKeyword::*;
1359 if *self == Self::ULTRA_CONDENSED {
1361 return Some(UltraCondensed);
1362 }
1363 if *self == Self::EXTRA_CONDENSED {
1364 return Some(ExtraCondensed);
1365 }
1366 if *self == Self::CONDENSED {
1367 return Some(Condensed);
1368 }
1369 if *self == Self::SEMI_CONDENSED {
1370 return Some(SemiCondensed);
1371 }
1372 if *self == Self::NORMAL {
1373 return Some(Normal);
1374 }
1375 if *self == Self::SEMI_EXPANDED {
1376 return Some(SemiExpanded);
1377 }
1378 if *self == Self::EXPANDED {
1379 return Some(Expanded);
1380 }
1381 if *self == Self::EXTRA_EXPANDED {
1382 return Some(ExtraExpanded);
1383 }
1384 if *self == Self::ULTRA_EXPANDED {
1385 return Some(UltraExpanded);
1386 }
1387 None
1388 }
1389}
1390
1391impl ToCss for FontStretch {
1392 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1393 where
1394 W: fmt::Write,
1395 {
1396 self.to_percentage().to_css(dest)
1397 }
1398}
1399
1400impl ToTyped for FontStretch {
1401 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1402 match self.as_keyword() {
1403 Some(keyword) => keyword.to_typed(dest),
1404 None => self.to_percentage().to_typed(dest),
1405 }
1406 }
1407}
1408
1409impl ToAnimatedValue for FontStretch {
1410 type AnimatedValue = Percentage;
1411
1412 #[inline]
1413 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1414 self.to_percentage()
1415 }
1416
1417 #[inline]
1418 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1419 Self::from_percentage(animated.0)
1420 }
1421}
1422
1423pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1425
1426impl ToResolvedValue for LineHeight {
1427 type ResolvedValue = Self;
1428
1429 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1430 #[cfg(feature = "gecko")]
1431 {
1432 if matches!(self, Self::Normal | Self::MozBlockHeight) {
1434 return self;
1435 }
1436 let wm = context.style.writing_mode;
1437 Self::Length(
1438 context
1439 .device
1440 .calc_line_height(
1441 context.style.get_font(),
1442 wm,
1443 Some(context.element_info.element),
1444 )
1445 .to_resolved_value(context),
1446 )
1447 }
1448 #[cfg(feature = "servo")]
1449 {
1450 if let LineHeight::Number(num) = &self {
1451 let size = context.style.get_font().clone_font_size().computed_size();
1452 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1453 } else {
1454 self
1455 }
1456 }
1457 }
1458
1459 #[inline]
1460 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1461 value
1462 }
1463}