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