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