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