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))]
270#[typed_value(derive_fields)]
271pub struct FontSize {
273 pub computed_size: NonNegativeLength,
276 #[css(skip)]
279 pub used_size: NonNegativeLength,
280 #[css(skip)]
282 pub keyword_info: KeywordInfo,
283}
284
285impl FontSize {
286 #[inline]
288 pub fn computed_size(&self) -> Length {
289 self.computed_size.0
290 }
291
292 #[inline]
294 pub fn used_size(&self) -> Length {
295 self.used_size.0
296 }
297
298 #[inline]
300 pub fn zoom(&self, zoom: Zoom) -> Self {
301 Self {
302 computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
303 used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
304 keyword_info: self.keyword_info,
305 }
306 }
307
308 #[inline]
309 pub fn medium() -> Self {
311 Self {
312 computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
313 used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
314 keyword_info: KeywordInfo::medium(),
315 }
316 }
317}
318
319impl ToAnimatedValue for FontSize {
320 type AnimatedValue = Length;
321
322 #[inline]
323 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
324 self.computed_size.0.to_animated_value(context)
325 }
326
327 #[inline]
328 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
329 FontSize {
330 computed_size: NonNegative(animated.clamp_to_non_negative()),
331 used_size: NonNegative(animated.clamp_to_non_negative()),
332 keyword_info: KeywordInfo::none(),
333 }
334 }
335}
336
337impl ToResolvedValue for FontSize {
338 type ResolvedValue = NonNegativeLength;
339
340 #[inline]
341 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
342 self.computed_size.to_resolved_value(context)
343 }
344
345 #[inline]
346 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
347 let computed_size = NonNegativeLength::from_resolved_value(resolved);
348 Self {
349 computed_size,
350 used_size: computed_size,
351 keyword_info: KeywordInfo::none(),
352 }
353 }
354}
355
356#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue, ToTyped)]
357#[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))]
358#[repr(C)]
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#[value_info(other_values = "normal")]
1014pub struct FontLanguageOverride(pub u32);
1015
1016impl FontLanguageOverride {
1017 #[inline]
1018 pub fn normal() -> FontLanguageOverride {
1020 FontLanguageOverride(0)
1021 }
1022
1023 #[inline]
1025 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
1026 *storage = u32::to_be_bytes(self.0);
1027 let slice = if cfg!(debug_assertions) {
1029 std::str::from_utf8(&storage[..]).unwrap()
1030 } else {
1031 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
1032 };
1033 slice.trim_end()
1034 }
1035
1036 #[inline]
1039 pub unsafe fn from_u32(value: u32) -> Self {
1040 Self(value)
1041 }
1042}
1043
1044impl ToCss for FontLanguageOverride {
1045 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1046 where
1047 W: fmt::Write,
1048 {
1049 if self.0 == 0 {
1050 return dest.write_str("normal");
1051 }
1052 self.to_str(&mut [0; 4]).to_css(dest)
1053 }
1054}
1055
1056impl ToComputedValue for specified::MozScriptMinSize {
1057 type ComputedValue = MozScriptMinSize;
1058
1059 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1060 let base_size = FontBaseSize::InheritedStyle;
1063 let line_height_base = LineHeightBase::InheritedStyle;
1064 match self.0 {
1065 NoCalcLength::FontRelative(value) => {
1066 value.to_computed_value(cx, base_size, line_height_base)
1067 },
1068 NoCalcLength::ServoCharacterWidth(value) => {
1069 value.to_computed_value(base_size.resolve(cx).computed_size())
1070 },
1071 ref l => l.to_computed_value(cx),
1072 }
1073 }
1074
1075 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1076 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1077 }
1078}
1079
1080pub type MathDepth = i8;
1082
1083#[cfg(feature = "gecko")]
1084impl ToComputedValue for specified::MathDepth {
1085 type ComputedValue = MathDepth;
1086
1087 fn to_computed_value(&self, cx: &Context) -> i8 {
1088 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1089 use std::{cmp, i8};
1090
1091 let int = match *self {
1092 specified::MathDepth::AutoAdd => {
1093 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
1094 let style = cx.builder.get_parent_font().clone_math_style();
1095 if style == MathStyleValue::Compact {
1096 parent.saturating_add(1)
1097 } else {
1098 parent
1099 }
1100 },
1101 specified::MathDepth::Add(rel) => {
1102 let parent = cx.builder.get_parent_font().clone_math_depth();
1103 (parent as i32).saturating_add(rel.to_computed_value(cx))
1104 },
1105 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1106 };
1107 cmp::min(int, i8::MAX as i32) as i8
1108 }
1109
1110 fn from_computed_value(other: &i8) -> Self {
1111 let computed_value = *other as i32;
1112 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1113 }
1114}
1115
1116impl ToAnimatedValue for MathDepth {
1117 type AnimatedValue = CSSInteger;
1118
1119 #[inline]
1120 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1121 self.into()
1122 }
1123
1124 #[inline]
1125 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1126 use std::{cmp, i8};
1127 cmp::min(animated, i8::MAX as i32) as i8
1128 }
1129}
1130
1131pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1136
1137pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1140
1141#[derive(
1152 Clone,
1153 ComputeSquaredDistance,
1154 Copy,
1155 Debug,
1156 Eq,
1157 Hash,
1158 MallocSizeOf,
1159 PartialEq,
1160 PartialOrd,
1161 ToResolvedValue,
1162 ToTyped,
1163)]
1164#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1165#[repr(C)]
1166pub struct FontStyle(FontStyleFixedPoint);
1167
1168impl FontStyle {
1169 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1171 value: 0 << FONT_STYLE_FRACTION_BITS,
1172 });
1173
1174 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1176 value: 100 << FONT_STYLE_FRACTION_BITS,
1177 });
1178
1179 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1182
1183 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1185 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1186 });
1187
1188 #[inline]
1190 pub fn normal() -> Self {
1191 Self::NORMAL
1192 }
1193
1194 pub fn oblique(degrees: f32) -> Self {
1196 Self(FixedPoint::from_float(
1197 degrees
1198 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1199 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1200 ))
1201 }
1202
1203 pub fn oblique_degrees(&self) -> f32 {
1205 debug_assert_ne!(*self, Self::ITALIC);
1206 self.0.to_float()
1207 }
1208}
1209
1210impl ToCss for FontStyle {
1211 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1212 where
1213 W: fmt::Write,
1214 {
1215 if *self == Self::NORMAL {
1216 return dest.write_str("normal");
1217 }
1218 if *self == Self::ITALIC {
1219 return dest.write_str("italic");
1220 }
1221 dest.write_str("oblique")?;
1222 if *self != Self::OBLIQUE {
1223 dest.write_char(' ')?;
1225 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1226 }
1227 Ok(())
1228 }
1229}
1230
1231impl ToAnimatedValue for FontStyle {
1232 type AnimatedValue = generics::FontStyle<Angle>;
1233
1234 #[inline]
1235 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1236 if self == Self::ITALIC {
1237 return generics::FontStyle::Italic;
1238 }
1239 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1240 }
1241
1242 #[inline]
1243 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1244 match animated {
1245 generics::FontStyle::Italic => Self::ITALIC,
1246 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1247 }
1248 }
1249}
1250
1251pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
1258
1259pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
1262
1263#[derive(
1272 Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,
1273)]
1274#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1275#[repr(C)]
1276pub struct FontStretch(pub FontStretchFixedPoint);
1277
1278impl FontStretch {
1279 pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
1281 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
1283
1284 pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1286 value: 50 << Self::FRACTION_BITS,
1287 });
1288 pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1290 value: (62 << Self::FRACTION_BITS) + Self::HALF,
1291 });
1292 pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1294 value: 75 << Self::FRACTION_BITS,
1295 });
1296 pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1298 value: (87 << Self::FRACTION_BITS) + Self::HALF,
1299 });
1300 pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
1302 value: 100 << Self::FRACTION_BITS,
1303 });
1304 pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1306 value: (112 << Self::FRACTION_BITS) + Self::HALF,
1307 });
1308 pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1310 value: 125 << Self::FRACTION_BITS,
1311 });
1312 pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1314 value: 150 << Self::FRACTION_BITS,
1315 });
1316 pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1318 value: 200 << Self::FRACTION_BITS,
1319 });
1320
1321 pub fn hundred() -> Self {
1323 Self::NORMAL
1324 }
1325
1326 #[inline]
1328 pub fn to_percentage(&self) -> Percentage {
1329 Percentage(self.0.to_float() / 100.0)
1330 }
1331
1332 pub fn from_percentage(p: f32) -> Self {
1334 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
1335 }
1336
1337 pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
1340 use specified::FontStretchKeyword::*;
1341 match kw {
1342 UltraCondensed => Self::ULTRA_CONDENSED,
1343 ExtraCondensed => Self::EXTRA_CONDENSED,
1344 Condensed => Self::CONDENSED,
1345 SemiCondensed => Self::SEMI_CONDENSED,
1346 Normal => Self::NORMAL,
1347 SemiExpanded => Self::SEMI_EXPANDED,
1348 Expanded => Self::EXPANDED,
1349 ExtraExpanded => Self::EXTRA_EXPANDED,
1350 UltraExpanded => Self::ULTRA_EXPANDED,
1351 }
1352 }
1353
1354 pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
1356 use specified::FontStretchKeyword::*;
1357 if *self == Self::ULTRA_CONDENSED {
1359 return Some(UltraCondensed);
1360 }
1361 if *self == Self::EXTRA_CONDENSED {
1362 return Some(ExtraCondensed);
1363 }
1364 if *self == Self::CONDENSED {
1365 return Some(Condensed);
1366 }
1367 if *self == Self::SEMI_CONDENSED {
1368 return Some(SemiCondensed);
1369 }
1370 if *self == Self::NORMAL {
1371 return Some(Normal);
1372 }
1373 if *self == Self::SEMI_EXPANDED {
1374 return Some(SemiExpanded);
1375 }
1376 if *self == Self::EXPANDED {
1377 return Some(Expanded);
1378 }
1379 if *self == Self::EXTRA_EXPANDED {
1380 return Some(ExtraExpanded);
1381 }
1382 if *self == Self::ULTRA_EXPANDED {
1383 return Some(UltraExpanded);
1384 }
1385 None
1386 }
1387}
1388
1389impl ToCss for FontStretch {
1390 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1391 where
1392 W: fmt::Write,
1393 {
1394 self.to_percentage().to_css(dest)
1395 }
1396}
1397
1398impl ToTyped for FontStretch {
1399 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1400 match self.as_keyword() {
1401 Some(keyword) => keyword.to_typed(dest),
1402 None => self.to_percentage().to_typed(dest),
1403 }
1404 }
1405}
1406
1407impl ToAnimatedValue for FontStretch {
1408 type AnimatedValue = Percentage;
1409
1410 #[inline]
1411 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1412 self.to_percentage()
1413 }
1414
1415 #[inline]
1416 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1417 Self::from_percentage(animated.0)
1418 }
1419}
1420
1421pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1423
1424impl ToResolvedValue for LineHeight {
1425 type ResolvedValue = Self;
1426
1427 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1428 #[cfg(feature = "gecko")]
1429 {
1430 if matches!(self, Self::Normal | Self::MozBlockHeight) {
1432 return self;
1433 }
1434 let wm = context.style.writing_mode;
1435 Self::Length(
1436 context
1437 .device
1438 .calc_line_height(
1439 context.style.get_font(),
1440 wm,
1441 Some(context.element_info.element),
1442 )
1443 .to_resolved_value(context),
1444 )
1445 }
1446 #[cfg(feature = "servo")]
1447 {
1448 if let LineHeight::Number(num) = &self {
1449 let size = context.style.get_font().clone_font_size().computed_size();
1450 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1451 } else {
1452 self
1453 }
1454 }
1455 }
1456
1457 #[inline]
1458 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1459 value
1460 }
1461}