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 static_prefs::pref!("layout.css.system-ui.enabled")
636}
637
638#[cfg(feature = "gecko")]
639fn math_enabled(context: &ParserContext) -> bool {
640 context.chrome_rules_enabled() || static_prefs::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<'i, 't>(
710 context: &ParserContext,
711 input: &mut Parser<'i, 't>,
712 ) -> Result<Self, ParseError<'i>> {
713 if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
714 return Ok(SingleFontFamily::FamilyName(FamilyName {
715 name: Atom::from(&*value),
716 syntax: FontFamilyNameSyntax::Quoted,
717 }));
718 }
719
720 if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
721 return Ok(SingleFontFamily::Generic(generic));
722 }
723
724 let first_ident = input.expect_ident_cloned()?;
725 let reserved = match_ignore_ascii_case! { &first_ident,
726 "inherit" | "initial" | "unset" | "revert" | "default" => true,
734 _ => false,
735 };
736
737 let mut value = first_ident.as_ref().to_owned();
738 let mut serialize_quoted = value.contains(' ');
739
740 if reserved {
743 let ident = input.expect_ident()?;
744 serialize_quoted = serialize_quoted || ident.contains(' ');
745 value.push(' ');
746 value.push_str(&ident);
747 }
748 while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
749 serialize_quoted = serialize_quoted || ident.contains(' ');
750 value.push(' ');
751 value.push_str(&ident);
752 }
753 let syntax = if serialize_quoted {
754 FontFamilyNameSyntax::Quoted
759 } else {
760 FontFamilyNameSyntax::Identifiers
761 };
762 Ok(SingleFontFamily::FamilyName(FamilyName {
763 name: Atom::from(value),
764 syntax,
765 }))
766 }
767}
768
769#[derive(
771 Clone,
772 Debug,
773 Deserialize,
774 Hash,
775 Serialize,
776 ToComputedValue,
777 ToResolvedValue,
778 ToShmem,
779 PartialEq,
780 Eq,
781)]
782#[repr(C)]
783pub struct FontFamilyList {
784 pub list: crate::ArcSlice<SingleFontFamily>,
786}
787
788impl FontFamilyList {
789 pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
791 self.list.iter()
792 }
793
794 #[cfg_attr(feature = "servo", allow(unused))]
801 pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
802 let mut index_of_first_generic = None;
803 let mut target_index = None;
804
805 for (i, f) in self.iter().enumerate() {
806 match &*f {
807 SingleFontFamily::Generic(f) => {
808 if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
809 if target_index.is_none() {
813 return;
814 }
815 index_of_first_generic = Some(i);
816 break;
817 }
818 if target_index.is_none() {
821 target_index = Some(i);
822 }
823 },
824 SingleFontFamily::FamilyName(fam) => {
825 if target_index.is_none() && !fam.is_known_icon_font_family() {
828 target_index = Some(i);
829 }
830 },
831 }
832 }
833
834 let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
835 let first_generic = match index_of_first_generic {
836 Some(i) => new_list.remove(i),
837 None => SingleFontFamily::Generic(generic),
838 };
839
840 if let Some(i) = target_index {
841 new_list.insert(i, first_generic);
842 } else {
843 new_list.push(first_generic);
844 }
845 self.list = crate::ArcSlice::from_iter(new_list.into_iter());
846 }
847
848 #[cfg_attr(feature = "servo", allow(unused))]
850 pub(crate) fn needs_user_font_prioritization(&self) -> bool {
851 self.iter().next().map_or(true, |f| match f {
852 SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
853 _ => true,
854 })
855 }
856
857 pub fn single_generic(&self) -> Option<GenericFontFamily> {
859 let mut iter = self.iter();
860 if let Some(SingleFontFamily::Generic(f)) = iter.next() {
861 if iter.next().is_none() {
862 return Some(*f);
863 }
864 }
865 None
866 }
867}
868
869pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
871
872impl FontSizeAdjust {
873 #[inline]
874 pub fn none() -> Self {
876 FontSizeAdjust::None
877 }
878}
879
880impl ToComputedValue for specified::FontSizeAdjust {
881 type ComputedValue = FontSizeAdjust;
882
883 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
884 use crate::font_metrics::FontMetricsOrientation;
885
886 let font_metrics = |vertical, flags| {
887 let orient = if vertical {
888 FontMetricsOrientation::MatchContextPreferVertical
889 } else {
890 FontMetricsOrientation::Horizontal
891 };
892 let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
893 let font_size = context.style().get_font().clone_font_size().used_size.0;
894 (metrics, font_size)
895 };
896
897 macro_rules! resolve {
901 ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
902 match $value {
903 specified::FontSizeAdjustFactor::Number(f) => {
904 FontSizeAdjust::$basis(f.to_computed_value(context))
905 },
906 specified::FontSizeAdjustFactor::FromFont => {
907 let (metrics, font_size) = font_metrics($vertical, $flags);
908 let ratio = if let Some(metric) = metrics.$field {
909 metric / font_size
910 } else if $fallback >= 0.0 {
911 $fallback
912 } else {
913 metrics.ascent / font_size
914 };
915 if ratio.is_nan() {
916 FontSizeAdjust::$basis(NonNegative(abs($fallback)))
917 } else {
918 FontSizeAdjust::$basis(NonNegative(ratio))
919 }
920 },
921 }
922 }};
923 }
924
925 match self {
926 Self::None => FontSizeAdjust::None,
927 Self::ExHeight(val) => {
928 resolve!(
929 ExHeight,
930 val,
931 false,
932 x_height,
933 0.5,
934 QueryFontMetricsFlags::empty()
935 )
936 },
937 Self::CapHeight(val) => {
938 resolve!(
939 CapHeight,
940 val,
941 false,
942 cap_height,
943 -1.0, QueryFontMetricsFlags::empty()
945 )
946 },
947 Self::ChWidth(val) => {
948 resolve!(
949 ChWidth,
950 val,
951 false,
952 zero_advance_measure,
953 0.5,
954 QueryFontMetricsFlags::NEEDS_CH
955 )
956 },
957 Self::IcWidth(val) => {
958 resolve!(
959 IcWidth,
960 val,
961 false,
962 ic_width,
963 1.0,
964 QueryFontMetricsFlags::NEEDS_IC
965 )
966 },
967 Self::IcHeight(val) => {
968 resolve!(
969 IcHeight,
970 val,
971 true,
972 ic_width,
973 1.0,
974 QueryFontMetricsFlags::NEEDS_IC
975 )
976 },
977 }
978 }
979
980 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
981 macro_rules! case {
982 ($basis:ident, $val:expr) => {
983 Self::$basis(specified::FontSizeAdjustFactor::Number(
984 ToComputedValue::from_computed_value($val),
985 ))
986 };
987 }
988 match *computed {
989 FontSizeAdjust::None => Self::None,
990 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
991 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
992 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
993 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
994 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
995 }
996 }
997}
998
999pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
1001
1002pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
1004
1005fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
1008where
1009 T: TaggedFontValue,
1010{
1011 if settings_list.len() > 1 {
1012 settings_list.sort_by_key(|k| k.tag().0);
1013 settings_list.dedup_by(|a, b| {
1016 if a.tag() == b.tag() {
1017 std::mem::swap(a, b);
1018 true
1019 } else {
1020 false
1021 }
1022 });
1023 }
1024}
1025
1026impl<T> ToComputedValue for FontSettings<T>
1027where
1028 T: ToComputedValue,
1029 <T as ToComputedValue>::ComputedValue: TaggedFontValue,
1030{
1031 type ComputedValue = FontSettings<T::ComputedValue>;
1032
1033 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
1034 let mut v = self
1035 .0
1036 .iter()
1037 .map(|item| item.to_computed_value(context))
1038 .collect::<Vec<_>>();
1039 dedup_font_settings(&mut v);
1040 FontSettings(v.into_boxed_slice())
1041 }
1042
1043 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
1044 Self(computed.0.iter().map(T::from_computed_value).collect())
1045 }
1046}
1047
1048#[derive(
1053 Clone,
1054 Copy,
1055 Debug,
1056 Deserialize,
1057 Eq,
1058 MallocSizeOf,
1059 PartialEq,
1060 Serialize,
1061 SpecifiedValueInfo,
1062 ToComputedValue,
1063 ToResolvedValue,
1064 ToShmem,
1065 ToTyped,
1066)]
1067#[repr(C)]
1068#[typed(todo_derive_fields)]
1069#[value_info(other_values = "normal")]
1070pub struct FontLanguageOverride(pub u32);
1071
1072impl FontLanguageOverride {
1073 #[inline]
1074 pub fn normal() -> FontLanguageOverride {
1076 FontLanguageOverride(0)
1077 }
1078
1079 #[inline]
1081 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
1082 *storage = u32::to_be_bytes(self.0);
1083 let slice = if cfg!(debug_assertions) {
1085 std::str::from_utf8(&storage[..]).unwrap()
1086 } else {
1087 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
1088 };
1089 slice.trim_end()
1090 }
1091
1092 #[inline]
1095 pub unsafe fn from_u32(value: u32) -> Self {
1096 Self(value)
1097 }
1098}
1099
1100impl ToCss for FontLanguageOverride {
1101 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1102 where
1103 W: fmt::Write,
1104 {
1105 if self.0 == 0 {
1106 return dest.write_str("normal");
1107 }
1108 self.to_str(&mut [0; 4]).to_css(dest)
1109 }
1110}
1111
1112impl ToComputedValue for specified::MozScriptMinSize {
1113 type ComputedValue = MozScriptMinSize;
1114
1115 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1116 let base_size = FontBaseSize::InheritedStyle;
1119 let line_height_base = LineHeightBase::InheritedStyle;
1120 self.0
1121 .to_computed_value_with_base_size(cx, base_size, line_height_base)
1122 }
1123
1124 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1125 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1126 }
1127}
1128
1129pub type MathDepth = i8;
1131
1132impl ToComputedValue for specified::MathDepth {
1133 type ComputedValue = MathDepth;
1134
1135 fn to_computed_value(&self, cx: &Context) -> i8 {
1136 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1137 use std::{cmp, i8};
1138
1139 let int = match self {
1140 specified::MathDepth::AutoAdd => {
1141 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
1142 let style = cx.builder.get_parent_font().clone_math_style();
1143 if style == MathStyleValue::Compact {
1144 parent.saturating_add(1)
1145 } else {
1146 parent
1147 }
1148 },
1149 specified::MathDepth::Add(rel) => {
1150 let parent = cx.builder.get_parent_font().clone_math_depth();
1151 (parent as i32).saturating_add(rel.to_computed_value(cx))
1152 },
1153 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1154 };
1155 cmp::min(int, i8::MAX as i32) as i8
1156 }
1157
1158 fn from_computed_value(other: &i8) -> Self {
1159 let computed_value = *other as i32;
1160 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1161 }
1162}
1163
1164impl ToAnimatedValue for MathDepth {
1165 type AnimatedValue = CSSInteger;
1166
1167 #[inline]
1168 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1169 self.into()
1170 }
1171
1172 #[inline]
1173 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1174 use std::{cmp, i8};
1175 cmp::min(animated, i8::MAX as i32) as i8
1176 }
1177}
1178
1179pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1184
1185pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1188
1189#[derive(
1200 Clone,
1201 ComputeSquaredDistance,
1202 Copy,
1203 Debug,
1204 Deserialize,
1205 Eq,
1206 Hash,
1207 MallocSizeOf,
1208 PartialEq,
1209 PartialOrd,
1210 Serialize,
1211 ToResolvedValue,
1212 ToTyped,
1213)]
1214#[repr(C)]
1215#[typed(todo_derive_fields)]
1216pub struct FontStyle(FontStyleFixedPoint);
1217
1218impl FontStyle {
1219 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1221 value: 0 << FONT_STYLE_FRACTION_BITS,
1222 });
1223
1224 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1226 value: 100 << FONT_STYLE_FRACTION_BITS,
1227 });
1228
1229 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1232
1233 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1235 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1236 });
1237
1238 #[inline]
1240 pub fn normal() -> Self {
1241 Self::NORMAL
1242 }
1243
1244 pub fn oblique(degrees: f32) -> Self {
1246 Self(FixedPoint::from_float(
1247 degrees
1248 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1249 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1250 ))
1251 }
1252
1253 pub fn oblique_degrees(&self) -> f32 {
1255 debug_assert_ne!(*self, Self::ITALIC);
1256 self.0.to_float()
1257 }
1258}
1259
1260impl ToCss for FontStyle {
1261 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1262 where
1263 W: fmt::Write,
1264 {
1265 if *self == Self::NORMAL {
1266 return dest.write_str("normal");
1267 }
1268 if *self == Self::ITALIC {
1269 return dest.write_str("italic");
1270 }
1271 dest.write_str("oblique")?;
1272 if *self != Self::OBLIQUE {
1273 dest.write_char(' ')?;
1275 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1276 }
1277 Ok(())
1278 }
1279}
1280
1281impl ToAnimatedValue for FontStyle {
1282 type AnimatedValue = generics::FontStyle<Angle>;
1283
1284 #[inline]
1285 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1286 if self == Self::ITALIC {
1287 return generics::FontStyle::Italic;
1288 }
1289 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1290 }
1291
1292 #[inline]
1293 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1294 match animated {
1295 generics::FontStyle::Italic => Self::ITALIC,
1296 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1297 }
1298 }
1299}
1300
1301pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
1308
1309pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
1312
1313#[derive(
1322 Clone,
1323 ComputeSquaredDistance,
1324 Copy,
1325 Debug,
1326 Deserialize,
1327 Hash,
1328 MallocSizeOf,
1329 PartialEq,
1330 PartialOrd,
1331 Serialize,
1332 ToResolvedValue,
1333)]
1334#[repr(C)]
1335pub struct FontStretch(pub FontStretchFixedPoint);
1336
1337impl FontStretch {
1338 pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
1340 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
1342
1343 pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1345 value: 50 << Self::FRACTION_BITS,
1346 });
1347 pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1349 value: (62 << Self::FRACTION_BITS) + Self::HALF,
1350 });
1351 pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1353 value: 75 << Self::FRACTION_BITS,
1354 });
1355 pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1357 value: (87 << Self::FRACTION_BITS) + Self::HALF,
1358 });
1359 pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
1361 value: 100 << Self::FRACTION_BITS,
1362 });
1363 pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1365 value: (112 << Self::FRACTION_BITS) + Self::HALF,
1366 });
1367 pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1369 value: 125 << Self::FRACTION_BITS,
1370 });
1371 pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1373 value: 150 << Self::FRACTION_BITS,
1374 });
1375 pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1377 value: 200 << Self::FRACTION_BITS,
1378 });
1379
1380 pub fn hundred() -> Self {
1382 Self::NORMAL
1383 }
1384
1385 #[inline]
1387 pub fn to_percentage(&self) -> Percentage {
1388 Percentage(self.0.to_float() / 100.0)
1389 }
1390
1391 pub fn from_percentage(p: f32) -> Self {
1393 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
1394 }
1395
1396 pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
1399 use specified::FontStretchKeyword::*;
1400 match kw {
1401 UltraCondensed => Self::ULTRA_CONDENSED,
1402 ExtraCondensed => Self::EXTRA_CONDENSED,
1403 Condensed => Self::CONDENSED,
1404 SemiCondensed => Self::SEMI_CONDENSED,
1405 Normal => Self::NORMAL,
1406 SemiExpanded => Self::SEMI_EXPANDED,
1407 Expanded => Self::EXPANDED,
1408 ExtraExpanded => Self::EXTRA_EXPANDED,
1409 UltraExpanded => Self::ULTRA_EXPANDED,
1410 }
1411 }
1412
1413 pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
1415 use specified::FontStretchKeyword::*;
1416 if *self == Self::ULTRA_CONDENSED {
1418 return Some(UltraCondensed);
1419 }
1420 if *self == Self::EXTRA_CONDENSED {
1421 return Some(ExtraCondensed);
1422 }
1423 if *self == Self::CONDENSED {
1424 return Some(Condensed);
1425 }
1426 if *self == Self::SEMI_CONDENSED {
1427 return Some(SemiCondensed);
1428 }
1429 if *self == Self::NORMAL {
1430 return Some(Normal);
1431 }
1432 if *self == Self::SEMI_EXPANDED {
1433 return Some(SemiExpanded);
1434 }
1435 if *self == Self::EXPANDED {
1436 return Some(Expanded);
1437 }
1438 if *self == Self::EXTRA_EXPANDED {
1439 return Some(ExtraExpanded);
1440 }
1441 if *self == Self::ULTRA_EXPANDED {
1442 return Some(UltraExpanded);
1443 }
1444 None
1445 }
1446}
1447
1448impl ToCss for FontStretch {
1449 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1450 where
1451 W: fmt::Write,
1452 {
1453 self.to_percentage().to_css(dest)
1454 }
1455}
1456
1457impl ToTyped for FontStretch {
1458 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
1459 match self.as_keyword() {
1460 Some(keyword) => keyword.to_typed(dest),
1461 None => self.to_percentage().to_typed(dest),
1462 }
1463 }
1464}
1465
1466impl ToAnimatedValue for FontStretch {
1467 type AnimatedValue = Percentage;
1468
1469 #[inline]
1470 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1471 self.to_percentage()
1472 }
1473
1474 #[inline]
1475 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1476 Self::from_percentage(animated.0)
1477 }
1478}
1479
1480pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1482
1483impl ToResolvedValue for LineHeight {
1484 type ResolvedValue = Self;
1485
1486 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1487 #[cfg(feature = "gecko")]
1488 {
1489 if matches!(self, Self::Normal) {
1491 return self;
1492 }
1493 let wm = context.style.writing_mode;
1494 Self::Length(
1495 context
1496 .device
1497 .calc_line_height(
1498 context.style.get_font(),
1499 wm,
1500 Some(context.element_info.element),
1501 )
1502 .to_resolved_value(context),
1503 )
1504 }
1505 #[cfg(feature = "servo")]
1506 {
1507 if let LineHeight::Number(num) = &self {
1508 let size = context.style.get_font().clone_font_size().computed_size();
1509 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1510 } else {
1511 self
1512 }
1513 }
1514 }
1515
1516 #[inline]
1517 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1518 value
1519 }
1520}