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};
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))]
264#[typed_value(derive_fields)]
265pub struct FontSize {
267 pub computed_size: NonNegativeLength,
270 #[css(skip)]
273 pub used_size: NonNegativeLength,
274 #[css(skip)]
276 pub keyword_info: KeywordInfo,
277}
278
279impl FontSize {
280 #[inline]
282 pub fn computed_size(&self) -> Length {
283 self.computed_size.0
284 }
285
286 #[inline]
288 pub fn used_size(&self) -> Length {
289 self.used_size.0
290 }
291
292 #[inline]
294 pub fn zoom(&self, zoom: Zoom) -> Self {
295 Self {
296 computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
297 used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
298 keyword_info: self.keyword_info,
299 }
300 }
301
302 #[inline]
303 pub fn medium() -> Self {
305 Self {
306 computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
307 used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
308 keyword_info: KeywordInfo::medium(),
309 }
310 }
311}
312
313impl ToAnimatedValue for FontSize {
314 type AnimatedValue = Length;
315
316 #[inline]
317 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
318 self.computed_size.0.to_animated_value(context)
319 }
320
321 #[inline]
322 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
323 FontSize {
324 computed_size: NonNegative(animated.clamp_to_non_negative()),
325 used_size: NonNegative(animated.clamp_to_non_negative()),
326 keyword_info: KeywordInfo::none(),
327 }
328 }
329}
330
331impl ToResolvedValue for FontSize {
332 type ResolvedValue = NonNegativeLength;
333
334 #[inline]
335 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
336 self.computed_size.to_resolved_value(context)
337 }
338
339 #[inline]
340 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
341 let computed_size = NonNegativeLength::from_resolved_value(resolved);
342 Self {
343 computed_size,
344 used_size: computed_size,
345 keyword_info: KeywordInfo::none(),
346 }
347 }
348}
349
350#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue, ToTyped)]
351#[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))]
352#[repr(C)]
354pub struct FontFamily {
355 pub families: FontFamilyList,
357 pub is_system_font: bool,
359 pub is_initial: bool,
362}
363
364macro_rules! static_font_family {
365 ($ident:ident, $family:expr) => {
366 static $ident: std::sync::LazyLock<FontFamily> = std::sync::LazyLock::new(|| FontFamily {
367 families: FontFamilyList {
368 list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
369 },
370 is_system_font: false,
371 is_initial: false,
372 });
373 };
374}
375
376impl FontFamily {
377 #[inline]
378 pub fn serif() -> Self {
380 Self::generic(GenericFontFamily::Serif).clone()
381 }
382
383 #[cfg(feature = "gecko")]
385 pub(crate) fn moz_bullet() -> &'static Self {
386 static_font_family!(
387 MOZ_BULLET,
388 SingleFontFamily::FamilyName(FamilyName {
389 name: atom!("-moz-bullet-font"),
390 syntax: FontFamilyNameSyntax::Identifiers,
391 })
392 );
393
394 &*MOZ_BULLET
395 }
396
397 #[cfg(feature = "gecko")]
399 pub fn for_system_font(name: &str) -> Self {
400 Self {
401 families: FontFamilyList {
402 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
403 FamilyName {
404 name: Atom::from(name),
405 syntax: FontFamilyNameSyntax::Identifiers,
406 },
407 ))),
408 },
409 is_system_font: true,
410 is_initial: false,
411 }
412 }
413
414 pub fn generic(generic: GenericFontFamily) -> &'static Self {
416 macro_rules! generic_font_family {
417 ($ident:ident, $family:ident) => {
418 static_font_family!(
419 $ident,
420 SingleFontFamily::Generic(GenericFontFamily::$family)
421 )
422 };
423 }
424
425 generic_font_family!(SERIF, Serif);
426 generic_font_family!(SANS_SERIF, SansSerif);
427 generic_font_family!(MONOSPACE, Monospace);
428 generic_font_family!(CURSIVE, Cursive);
429 generic_font_family!(FANTASY, Fantasy);
430 #[cfg(feature = "gecko")]
431 generic_font_family!(MATH, Math);
432 #[cfg(feature = "gecko")]
433 generic_font_family!(MOZ_EMOJI, MozEmoji);
434 generic_font_family!(SYSTEM_UI, SystemUi);
435
436 let family = match generic {
437 GenericFontFamily::None => {
438 debug_assert!(false, "Bogus caller!");
439 &*SERIF
440 },
441 GenericFontFamily::Serif => &*SERIF,
442 GenericFontFamily::SansSerif => &*SANS_SERIF,
443 GenericFontFamily::Monospace => &*MONOSPACE,
444 GenericFontFamily::Cursive => &*CURSIVE,
445 GenericFontFamily::Fantasy => &*FANTASY,
446 #[cfg(feature = "gecko")]
447 GenericFontFamily::Math => &*MATH,
448 #[cfg(feature = "gecko")]
449 GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
450 GenericFontFamily::SystemUi => &*SYSTEM_UI,
451 };
452 debug_assert_eq!(
453 *family.families.iter().next().unwrap(),
454 SingleFontFamily::Generic(generic)
455 );
456 family
457 }
458}
459
460impl MallocSizeOf for FontFamily {
461 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
462 use malloc_size_of::MallocUnconditionalSizeOf;
463 let shared_font_list = &self.families.list;
467 if shared_font_list.is_unique() {
468 shared_font_list.unconditional_size_of(ops)
469 } else {
470 0
471 }
472 }
473}
474
475impl ToCss for FontFamily {
476 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
477 where
478 W: fmt::Write,
479 {
480 let mut iter = self.families.iter();
481 match iter.next() {
482 Some(f) => f.to_css(dest)?,
483 None => return Ok(()),
484 }
485 for family in iter {
486 dest.write_str(", ")?;
487 family.to_css(dest)?;
488 }
489 Ok(())
490 }
491}
492
493#[derive(
495 Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
496)]
497#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
498#[repr(C)]
499pub struct FamilyName {
500 pub name: Atom,
502 pub syntax: FontFamilyNameSyntax,
504}
505
506#[cfg(feature = "gecko")]
507impl FamilyName {
508 fn is_known_icon_font_family(&self) -> bool {
509 use crate::gecko_bindings::bindings;
510 unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
511 }
512}
513
514#[cfg(feature = "servo")]
515impl FamilyName {
516 fn is_known_icon_font_family(&self) -> bool {
517 false
518 }
519}
520
521impl ToCss for FamilyName {
522 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
523 where
524 W: fmt::Write,
525 {
526 match self.syntax {
527 FontFamilyNameSyntax::Quoted => {
528 dest.write_char('"')?;
529 write!(CssStringWriter::new(dest), "{}", self.name)?;
530 dest.write_char('"')
531 },
532 FontFamilyNameSyntax::Identifiers => {
533 let mut first = true;
534 for ident in self.name.to_string().split(' ') {
535 if first {
536 first = false;
537 } else {
538 dest.write_char(' ')?;
539 }
540 debug_assert!(
541 !ident.is_empty(),
542 "Family name with leading, \
543 trailing, or consecutive white spaces should \
544 have been marked quoted by the parser"
545 );
546 serialize_identifier(ident, dest)?;
547 }
548 Ok(())
549 },
550 }
551 }
552}
553
554#[derive(
555 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
556)]
557#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
558#[repr(u8)]
561pub enum FontFamilyNameSyntax {
562 Quoted,
565
566 Identifiers,
569}
570
571#[derive(
574 Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
575)]
576#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
577#[repr(u8)]
578pub enum SingleFontFamily {
579 FamilyName(FamilyName),
581 Generic(GenericFontFamily),
583}
584
585fn system_ui_enabled(_: &ParserContext) -> bool {
586 static_prefs::pref!("layout.css.system-ui.enabled")
587}
588
589#[cfg(feature = "gecko")]
590fn math_enabled(context: &ParserContext) -> bool {
591 context.chrome_rules_enabled() || static_prefs::pref!("mathml.font_family_math.enabled")
592}
593
594#[derive(
604 Clone,
605 Copy,
606 Debug,
607 Eq,
608 Hash,
609 MallocSizeOf,
610 PartialEq,
611 Parse,
612 ToCss,
613 ToComputedValue,
614 ToResolvedValue,
615 ToShmem,
616)]
617#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
618#[repr(u32)]
619#[allow(missing_docs)]
620pub enum GenericFontFamily {
621 #[css(skip)]
625 None = 0,
626 Serif,
627 SansSerif,
628 #[parse(aliases = "-moz-fixed")]
629 Monospace,
630 Cursive,
631 Fantasy,
632 #[cfg(feature = "gecko")]
633 #[parse(condition = "math_enabled")]
634 Math,
635 #[parse(condition = "system_ui_enabled")]
636 SystemUi,
637 #[css(skip)]
639 #[cfg(feature = "gecko")]
640 MozEmoji,
641}
642
643impl GenericFontFamily {
644 pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
648 match self {
649 Self::None | Self::Cursive | Self::Fantasy | Self::SystemUi => false,
650 #[cfg(feature = "gecko")]
651 Self::Math | Self::MozEmoji => false,
652 Self::Serif | Self::SansSerif | Self::Monospace => true,
653 }
654 }
655}
656
657impl Parse for SingleFontFamily {
658 fn parse<'i, 't>(
660 context: &ParserContext,
661 input: &mut Parser<'i, 't>,
662 ) -> Result<Self, ParseError<'i>> {
663 if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
664 return Ok(SingleFontFamily::FamilyName(FamilyName {
665 name: Atom::from(&*value),
666 syntax: FontFamilyNameSyntax::Quoted,
667 }));
668 }
669
670 if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
671 return Ok(SingleFontFamily::Generic(generic));
672 }
673
674 let first_ident = input.expect_ident_cloned()?;
675 let reserved = match_ignore_ascii_case! { &first_ident,
676 "inherit" | "initial" | "unset" | "revert" | "default" => true,
684 _ => false,
685 };
686
687 let mut value = first_ident.as_ref().to_owned();
688 let mut serialize_quoted = value.contains(' ');
689
690 if reserved {
693 let ident = input.expect_ident()?;
694 serialize_quoted = serialize_quoted || ident.contains(' ');
695 value.push(' ');
696 value.push_str(&ident);
697 }
698 while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
699 serialize_quoted = serialize_quoted || ident.contains(' ');
700 value.push(' ');
701 value.push_str(&ident);
702 }
703 let syntax = if serialize_quoted {
704 FontFamilyNameSyntax::Quoted
709 } else {
710 FontFamilyNameSyntax::Identifiers
711 };
712 Ok(SingleFontFamily::FamilyName(FamilyName {
713 name: Atom::from(value),
714 syntax,
715 }))
716 }
717}
718
719#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
721#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
722#[repr(C)]
723pub struct FontFamilyList {
724 pub list: crate::ArcSlice<SingleFontFamily>,
726}
727
728impl FontFamilyList {
729 pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
731 self.list.iter()
732 }
733
734 #[cfg_attr(feature = "servo", allow(unused))]
741 pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
742 let mut index_of_first_generic = None;
743 let mut target_index = None;
744
745 for (i, f) in self.iter().enumerate() {
746 match &*f {
747 SingleFontFamily::Generic(f) => {
748 if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
749 if target_index.is_none() {
753 return;
754 }
755 index_of_first_generic = Some(i);
756 break;
757 }
758 if target_index.is_none() {
761 target_index = Some(i);
762 }
763 },
764 SingleFontFamily::FamilyName(fam) => {
765 if target_index.is_none() && !fam.is_known_icon_font_family() {
768 target_index = Some(i);
769 }
770 },
771 }
772 }
773
774 let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
775 let first_generic = match index_of_first_generic {
776 Some(i) => new_list.remove(i),
777 None => SingleFontFamily::Generic(generic),
778 };
779
780 if let Some(i) = target_index {
781 new_list.insert(i, first_generic);
782 } else {
783 new_list.push(first_generic);
784 }
785 self.list = crate::ArcSlice::from_iter(new_list.into_iter());
786 }
787
788 #[cfg_attr(feature = "servo", allow(unused))]
790 pub(crate) fn needs_user_font_prioritization(&self) -> bool {
791 self.iter().next().map_or(true, |f| match f {
792 SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
793 _ => true,
794 })
795 }
796
797 pub fn single_generic(&self) -> Option<GenericFontFamily> {
799 let mut iter = self.iter();
800 if let Some(SingleFontFamily::Generic(f)) = iter.next() {
801 if iter.next().is_none() {
802 return Some(*f);
803 }
804 }
805 None
806 }
807}
808
809pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
811
812impl FontSizeAdjust {
813 #[inline]
814 pub fn none() -> Self {
816 FontSizeAdjust::None
817 }
818}
819
820impl ToComputedValue for specified::FontSizeAdjust {
821 type ComputedValue = FontSizeAdjust;
822
823 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
824 use crate::font_metrics::FontMetricsOrientation;
825
826 let font_metrics = |vertical, flags| {
827 let orient = if vertical {
828 FontMetricsOrientation::MatchContextPreferVertical
829 } else {
830 FontMetricsOrientation::Horizontal
831 };
832 let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
833 let font_size = context.style().get_font().clone_font_size().used_size.0;
834 (metrics, font_size)
835 };
836
837 macro_rules! resolve {
841 ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
842 match $value {
843 specified::FontSizeAdjustFactor::Number(f) => {
844 FontSizeAdjust::$basis(f.to_computed_value(context))
845 },
846 specified::FontSizeAdjustFactor::FromFont => {
847 let (metrics, font_size) = font_metrics($vertical, $flags);
848 let ratio = if let Some(metric) = metrics.$field {
849 metric / font_size
850 } else if $fallback >= 0.0 {
851 $fallback
852 } else {
853 metrics.ascent / font_size
854 };
855 if ratio.is_nan() {
856 FontSizeAdjust::$basis(NonNegative(abs($fallback)))
857 } else {
858 FontSizeAdjust::$basis(NonNegative(ratio))
859 }
860 },
861 }
862 }};
863 }
864
865 match *self {
866 Self::None => FontSizeAdjust::None,
867 Self::ExHeight(val) => {
868 resolve!(
869 ExHeight,
870 val,
871 false,
872 x_height,
873 0.5,
874 QueryFontMetricsFlags::empty()
875 )
876 },
877 Self::CapHeight(val) => {
878 resolve!(
879 CapHeight,
880 val,
881 false,
882 cap_height,
883 -1.0, QueryFontMetricsFlags::empty()
885 )
886 },
887 Self::ChWidth(val) => {
888 resolve!(
889 ChWidth,
890 val,
891 false,
892 zero_advance_measure,
893 0.5,
894 QueryFontMetricsFlags::NEEDS_CH
895 )
896 },
897 Self::IcWidth(val) => {
898 resolve!(
899 IcWidth,
900 val,
901 false,
902 ic_width,
903 1.0,
904 QueryFontMetricsFlags::NEEDS_IC
905 )
906 },
907 Self::IcHeight(val) => {
908 resolve!(
909 IcHeight,
910 val,
911 true,
912 ic_width,
913 1.0,
914 QueryFontMetricsFlags::NEEDS_IC
915 )
916 },
917 }
918 }
919
920 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
921 macro_rules! case {
922 ($basis:ident, $val:expr) => {
923 Self::$basis(specified::FontSizeAdjustFactor::Number(
924 ToComputedValue::from_computed_value($val),
925 ))
926 };
927 }
928 match *computed {
929 FontSizeAdjust::None => Self::None,
930 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
931 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
932 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
933 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
934 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
935 }
936 }
937}
938
939pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
941
942pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
944
945fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
948where
949 T: TaggedFontValue,
950{
951 if settings_list.len() > 1 {
952 settings_list.sort_by_key(|k| k.tag().0);
953 let mut prev_tag = settings_list.last().unwrap().tag();
956 for i in (0..settings_list.len() - 1).rev() {
957 let cur_tag = settings_list[i].tag();
958 if cur_tag == prev_tag {
959 settings_list.remove(i);
960 }
961 prev_tag = cur_tag;
962 }
963 }
964}
965
966impl<T> ToComputedValue for FontSettings<T>
967where
968 T: ToComputedValue,
969 <T as ToComputedValue>::ComputedValue: TaggedFontValue,
970{
971 type ComputedValue = FontSettings<T::ComputedValue>;
972
973 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
974 let mut v = self
975 .0
976 .iter()
977 .map(|item| item.to_computed_value(context))
978 .collect::<Vec<_>>();
979 dedup_font_settings(&mut v);
980 FontSettings(v.into_boxed_slice())
981 }
982
983 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
984 Self(computed.0.iter().map(T::from_computed_value).collect())
985 }
986}
987
988#[derive(
993 Clone,
994 Copy,
995 Debug,
996 Eq,
997 MallocSizeOf,
998 PartialEq,
999 SpecifiedValueInfo,
1000 ToComputedValue,
1001 ToResolvedValue,
1002 ToShmem,
1003 ToTyped,
1004)]
1005#[repr(C)]
1006#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1007#[value_info(other_values = "normal")]
1008pub struct FontLanguageOverride(pub u32);
1009
1010impl FontLanguageOverride {
1011 #[inline]
1012 pub fn normal() -> FontLanguageOverride {
1014 FontLanguageOverride(0)
1015 }
1016
1017 #[inline]
1019 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
1020 *storage = u32::to_be_bytes(self.0);
1021 let slice = if cfg!(debug_assertions) {
1023 std::str::from_utf8(&storage[..]).unwrap()
1024 } else {
1025 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
1026 };
1027 slice.trim_end()
1028 }
1029
1030 #[inline]
1033 pub unsafe fn from_u32(value: u32) -> Self {
1034 Self(value)
1035 }
1036}
1037
1038impl ToCss for FontLanguageOverride {
1039 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1040 where
1041 W: fmt::Write,
1042 {
1043 if self.0 == 0 {
1044 return dest.write_str("normal");
1045 }
1046 self.to_str(&mut [0; 4]).to_css(dest)
1047 }
1048}
1049
1050impl ToComputedValue for specified::MozScriptMinSize {
1051 type ComputedValue = MozScriptMinSize;
1052
1053 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1054 let base_size = FontBaseSize::InheritedStyle;
1057 let line_height_base = LineHeightBase::InheritedStyle;
1058 match self.0 {
1059 NoCalcLength::FontRelative(value) => {
1060 value.to_computed_value(cx, base_size, line_height_base)
1061 },
1062 NoCalcLength::ServoCharacterWidth(value) => {
1063 value.to_computed_value(base_size.resolve(cx).computed_size())
1064 },
1065 ref l => l.to_computed_value(cx),
1066 }
1067 }
1068
1069 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1070 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1071 }
1072}
1073
1074pub type MathDepth = i8;
1076
1077#[cfg(feature = "gecko")]
1078impl ToComputedValue for specified::MathDepth {
1079 type ComputedValue = MathDepth;
1080
1081 fn to_computed_value(&self, cx: &Context) -> i8 {
1082 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1083 use std::{cmp, i8};
1084
1085 let int = match *self {
1086 specified::MathDepth::AutoAdd => {
1087 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
1088 let style = cx.builder.get_parent_font().clone_math_style();
1089 if style == MathStyleValue::Compact {
1090 parent.saturating_add(1)
1091 } else {
1092 parent
1093 }
1094 },
1095 specified::MathDepth::Add(rel) => {
1096 let parent = cx.builder.get_parent_font().clone_math_depth();
1097 (parent as i32).saturating_add(rel.to_computed_value(cx))
1098 },
1099 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1100 };
1101 cmp::min(int, i8::MAX as i32) as i8
1102 }
1103
1104 fn from_computed_value(other: &i8) -> Self {
1105 let computed_value = *other as i32;
1106 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1107 }
1108}
1109
1110impl ToAnimatedValue for MathDepth {
1111 type AnimatedValue = CSSInteger;
1112
1113 #[inline]
1114 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1115 self.into()
1116 }
1117
1118 #[inline]
1119 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1120 use std::{cmp, i8};
1121 cmp::min(animated, i8::MAX as i32) as i8
1122 }
1123}
1124
1125pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1130
1131pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1134
1135#[derive(
1146 Clone,
1147 ComputeSquaredDistance,
1148 Copy,
1149 Debug,
1150 Eq,
1151 Hash,
1152 MallocSizeOf,
1153 PartialEq,
1154 PartialOrd,
1155 ToResolvedValue,
1156 ToTyped,
1157)]
1158#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1159#[repr(C)]
1160pub struct FontStyle(FontStyleFixedPoint);
1161
1162impl FontStyle {
1163 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1165 value: 0 << FONT_STYLE_FRACTION_BITS,
1166 });
1167
1168 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1170 value: 100 << FONT_STYLE_FRACTION_BITS,
1171 });
1172
1173 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1176
1177 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1179 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1180 });
1181
1182 #[inline]
1184 pub fn normal() -> Self {
1185 Self::NORMAL
1186 }
1187
1188 pub fn oblique(degrees: f32) -> Self {
1190 Self(FixedPoint::from_float(
1191 degrees
1192 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1193 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1194 ))
1195 }
1196
1197 pub fn oblique_degrees(&self) -> f32 {
1199 debug_assert_ne!(*self, Self::ITALIC);
1200 self.0.to_float()
1201 }
1202}
1203
1204impl ToCss for FontStyle {
1205 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1206 where
1207 W: fmt::Write,
1208 {
1209 if *self == Self::NORMAL {
1210 return dest.write_str("normal");
1211 }
1212 if *self == Self::ITALIC {
1213 return dest.write_str("italic");
1214 }
1215 dest.write_str("oblique")?;
1216 if *self != Self::OBLIQUE {
1217 dest.write_char(' ')?;
1219 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1220 }
1221 Ok(())
1222 }
1223}
1224
1225impl ToAnimatedValue for FontStyle {
1226 type AnimatedValue = generics::FontStyle<Angle>;
1227
1228 #[inline]
1229 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1230 if self == Self::ITALIC {
1231 return generics::FontStyle::Italic;
1232 }
1233 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1234 }
1235
1236 #[inline]
1237 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1238 match animated {
1239 generics::FontStyle::Italic => Self::ITALIC,
1240 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1241 }
1242 }
1243}
1244
1245pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
1252
1253pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
1256
1257#[derive(
1266 Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,
1267)]
1268#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1269#[repr(C)]
1270pub struct FontStretch(pub FontStretchFixedPoint);
1271
1272impl FontStretch {
1273 pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
1275 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
1277
1278 pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1280 value: 50 << Self::FRACTION_BITS,
1281 });
1282 pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1284 value: (62 << Self::FRACTION_BITS) + Self::HALF,
1285 });
1286 pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1288 value: 75 << Self::FRACTION_BITS,
1289 });
1290 pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1292 value: (87 << Self::FRACTION_BITS) + Self::HALF,
1293 });
1294 pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
1296 value: 100 << Self::FRACTION_BITS,
1297 });
1298 pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1300 value: (112 << Self::FRACTION_BITS) + Self::HALF,
1301 });
1302 pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1304 value: 125 << Self::FRACTION_BITS,
1305 });
1306 pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1308 value: 150 << Self::FRACTION_BITS,
1309 });
1310 pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1312 value: 200 << Self::FRACTION_BITS,
1313 });
1314
1315 pub fn hundred() -> Self {
1317 Self::NORMAL
1318 }
1319
1320 #[inline]
1322 pub fn to_percentage(&self) -> Percentage {
1323 Percentage(self.0.to_float() / 100.0)
1324 }
1325
1326 pub fn from_percentage(p: f32) -> Self {
1328 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
1329 }
1330
1331 pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
1334 use specified::FontStretchKeyword::*;
1335 match kw {
1336 UltraCondensed => Self::ULTRA_CONDENSED,
1337 ExtraCondensed => Self::EXTRA_CONDENSED,
1338 Condensed => Self::CONDENSED,
1339 SemiCondensed => Self::SEMI_CONDENSED,
1340 Normal => Self::NORMAL,
1341 SemiExpanded => Self::SEMI_EXPANDED,
1342 Expanded => Self::EXPANDED,
1343 ExtraExpanded => Self::EXTRA_EXPANDED,
1344 UltraExpanded => Self::ULTRA_EXPANDED,
1345 }
1346 }
1347
1348 pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
1350 use specified::FontStretchKeyword::*;
1351 if *self == Self::ULTRA_CONDENSED {
1353 return Some(UltraCondensed);
1354 }
1355 if *self == Self::EXTRA_CONDENSED {
1356 return Some(ExtraCondensed);
1357 }
1358 if *self == Self::CONDENSED {
1359 return Some(Condensed);
1360 }
1361 if *self == Self::SEMI_CONDENSED {
1362 return Some(SemiCondensed);
1363 }
1364 if *self == Self::NORMAL {
1365 return Some(Normal);
1366 }
1367 if *self == Self::SEMI_EXPANDED {
1368 return Some(SemiExpanded);
1369 }
1370 if *self == Self::EXPANDED {
1371 return Some(Expanded);
1372 }
1373 if *self == Self::EXTRA_EXPANDED {
1374 return Some(ExtraExpanded);
1375 }
1376 if *self == Self::ULTRA_EXPANDED {
1377 return Some(UltraExpanded);
1378 }
1379 None
1380 }
1381}
1382
1383impl ToCss for FontStretch {
1384 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1385 where
1386 W: fmt::Write,
1387 {
1388 self.to_percentage().to_css(dest)
1389 }
1390}
1391
1392impl ToTyped for FontStretch {
1393 fn to_typed(&self) -> Option<TypedValue> {
1394 self.as_keyword()
1395 .map_or(self.to_percentage().to_typed(), |keyword| {
1396 keyword.to_typed()
1397 })
1398 }
1399}
1400
1401impl ToAnimatedValue for FontStretch {
1402 type AnimatedValue = Percentage;
1403
1404 #[inline]
1405 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1406 self.to_percentage()
1407 }
1408
1409 #[inline]
1410 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1411 Self::from_percentage(animated.0)
1412 }
1413}
1414
1415pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1417
1418impl ToResolvedValue for LineHeight {
1419 type ResolvedValue = Self;
1420
1421 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1422 #[cfg(feature = "gecko")]
1423 {
1424 if matches!(self, Self::Normal | Self::MozBlockHeight) {
1426 return self;
1427 }
1428 let wm = context.style.writing_mode;
1429 Self::Length(
1430 context
1431 .device
1432 .calc_line_height(
1433 context.style.get_font(),
1434 wm,
1435 Some(context.element_info.element),
1436 )
1437 .to_resolved_value(context),
1438 )
1439 }
1440 #[cfg(feature = "servo")]
1441 {
1442 if let LineHeight::Number(num) = &self {
1443 let size = context.style.get_font().clone_font_size().computed_size();
1444 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1445 } else {
1446 self
1447 }
1448 }
1449 }
1450
1451 #[inline]
1452 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1453 value
1454 }
1455}