1use crate::parser::{Parse, ParserContext};
8use crate::values::animated::ToAnimatedValue;
9use crate::values::computed::{
10 Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,
11 ToComputedValue, Zoom,
12};
13use crate::values::generics::font::{
14 FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,
15};
16use crate::values::generics::{font as generics, NonNegative};
17use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
18use crate::values::specified::font::{
19 self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,
20};
21use crate::values::specified::length::{FontBaseSize, LineHeightBase, NoCalcLength};
22use crate::Atom;
23use cssparser::{serialize_identifier, CssStringWriter, Parser};
24use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
25use num_traits::abs;
26use num_traits::cast::AsPrimitive;
27use std::fmt::{self, Write};
28use style_traits::{CssWriter, ParseError, ToCss};
29
30pub use crate::values::computed::Length as MozScriptMinSize;
31pub use crate::values::specified::font::MozScriptSizeMultiplier;
32pub use crate::values::specified::font::{FontPalette, FontSynthesis, FontSynthesisStyle};
33pub use crate::values::specified::font::{
34 FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric,
35 QueryFontMetricsFlags, XLang, XTextScale,
36};
37pub use crate::values::specified::Integer as SpecifiedInteger;
38pub use crate::values::specified::Number as SpecifiedNumber;
39
40#[repr(C)]
59#[derive(
60 Clone,
61 ComputeSquaredDistance,
62 Copy,
63 Debug,
64 Eq,
65 Hash,
66 MallocSizeOf,
67 PartialEq,
68 PartialOrd,
69 ToResolvedValue,
70)]
71#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
72pub struct FixedPoint<T, const FRACTION_BITS: u16> {
73 pub value: T,
75}
76
77impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
78where
79 T: AsPrimitive<f32>,
80 f32: AsPrimitive<T>,
81 u16: AsPrimitive<T>,
82{
83 const SCALE: u16 = 1 << FRACTION_BITS;
84 const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;
85
86 pub fn from_float(v: f32) -> Self {
88 Self {
89 value: (v * Self::SCALE as f32).round().as_(),
90 }
91 }
92
93 pub fn to_float(&self) -> f32 {
95 self.value.as_() * Self::INVERSE_SCALE
96 }
97}
98
99impl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> {
102 type Output = Self;
103 fn div(self, rhs: Self) -> Self {
104 Self {
105 value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16,
106 }
107 }
108}
109impl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> {
110 type Output = Self;
111 fn mul(self, rhs: Self) -> Self {
112 Self {
113 value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16,
114 }
115 }
116}
117
118pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
123
124pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;
127
128#[derive(
137 Clone,
138 ComputeSquaredDistance,
139 Copy,
140 Debug,
141 Hash,
142 MallocSizeOf,
143 PartialEq,
144 PartialOrd,
145 ToResolvedValue,
146 ToTyped,
147)]
148#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
149#[repr(C)]
150pub struct FontWeight(FontWeightFixedPoint);
151impl ToAnimatedValue for FontWeight {
152 type AnimatedValue = Number;
153
154 #[inline]
155 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
156 self.value()
157 }
158
159 #[inline]
160 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
161 FontWeight::from_float(animated)
162 }
163}
164
165impl ToCss for FontWeight {
166 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
167 where
168 W: fmt::Write,
169 {
170 self.value().to_css(dest)
171 }
172}
173
174impl FontWeight {
175 pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
177 value: 400 << FONT_WEIGHT_FRACTION_BITS,
178 });
179
180 pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
182 value: 700 << FONT_WEIGHT_FRACTION_BITS,
183 });
184
185 pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
187 value: 600 << FONT_WEIGHT_FRACTION_BITS,
188 });
189
190 pub fn normal() -> Self {
192 Self::NORMAL
193 }
194
195 pub fn is_bold(&self) -> bool {
197 *self >= Self::BOLD_THRESHOLD
198 }
199
200 pub fn value(&self) -> f32 {
202 self.0.to_float()
203 }
204
205 pub fn from_float(v: f32) -> Self {
207 Self(FixedPoint::from_float(
208 v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
209 ))
210 }
211
212 pub fn bolder(self) -> Self {
217 let value = self.value();
218 if value < 350. {
219 return Self::NORMAL;
220 }
221 if value < 550. {
222 return Self::BOLD;
223 }
224 Self::from_float(value.max(900.))
225 }
226
227 pub fn lighter(self) -> Self {
232 let value = self.value();
233 if value < 550. {
234 return Self::from_float(value.min(100.));
235 }
236 if value < 750. {
237 return Self::NORMAL;
238 }
239 Self::BOLD
240 }
241}
242
243#[derive(
244 Animate,
245 Clone,
246 ComputeSquaredDistance,
247 Copy,
248 Debug,
249 MallocSizeOf,
250 PartialEq,
251 ToAnimatedZero,
252 ToCss,
253 ToTyped,
254)]
255#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
256pub struct FontSize {
258 pub computed_size: NonNegativeLength,
261 #[css(skip)]
264 pub used_size: NonNegativeLength,
265 #[css(skip)]
267 pub keyword_info: KeywordInfo,
268}
269
270impl FontSize {
271 #[inline]
273 pub fn computed_size(&self) -> Length {
274 self.computed_size.0
275 }
276
277 #[inline]
279 pub fn used_size(&self) -> Length {
280 self.used_size.0
281 }
282
283 #[inline]
285 pub fn zoom(&self, zoom: Zoom) -> Self {
286 Self {
287 computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
288 used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
289 keyword_info: self.keyword_info,
290 }
291 }
292
293 #[inline]
294 pub fn medium() -> Self {
296 Self {
297 computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
298 used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
299 keyword_info: KeywordInfo::medium(),
300 }
301 }
302}
303
304impl ToAnimatedValue for FontSize {
305 type AnimatedValue = Length;
306
307 #[inline]
308 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
309 self.computed_size.0.to_animated_value(context)
310 }
311
312 #[inline]
313 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
314 FontSize {
315 computed_size: NonNegative(animated.clamp_to_non_negative()),
316 used_size: NonNegative(animated.clamp_to_non_negative()),
317 keyword_info: KeywordInfo::none(),
318 }
319 }
320}
321
322impl ToResolvedValue for FontSize {
323 type ResolvedValue = NonNegativeLength;
324
325 #[inline]
326 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
327 self.computed_size.to_resolved_value(context)
328 }
329
330 #[inline]
331 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
332 let computed_size = NonNegativeLength::from_resolved_value(resolved);
333 Self {
334 computed_size,
335 used_size: computed_size,
336 keyword_info: KeywordInfo::none(),
337 }
338 }
339}
340
341#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue, ToTyped)]
342#[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))]
343#[repr(C)]
345pub struct FontFamily {
346 pub families: FontFamilyList,
348 pub is_system_font: bool,
350 pub is_initial: bool,
353}
354
355macro_rules! static_font_family {
356 ($ident:ident, $family:expr) => {
357 lazy_static! {
358 static ref $ident: FontFamily = FontFamily {
359 families: FontFamilyList {
360 list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
361 },
362 is_system_font: false,
363 is_initial: false,
364 };
365 }
366 };
367}
368
369impl FontFamily {
370 #[inline]
371 pub fn serif() -> Self {
373 Self::generic(GenericFontFamily::Serif).clone()
374 }
375
376 #[cfg(feature = "gecko")]
378 pub(crate) fn moz_bullet() -> &'static Self {
379 static_font_family!(
380 MOZ_BULLET,
381 SingleFontFamily::FamilyName(FamilyName {
382 name: atom!("-moz-bullet-font"),
383 syntax: FontFamilyNameSyntax::Identifiers,
384 })
385 );
386
387 &*MOZ_BULLET
388 }
389
390 #[cfg(feature = "gecko")]
392 pub fn for_system_font(name: &str) -> Self {
393 Self {
394 families: FontFamilyList {
395 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
396 FamilyName {
397 name: Atom::from(name),
398 syntax: FontFamilyNameSyntax::Identifiers,
399 },
400 ))),
401 },
402 is_system_font: true,
403 is_initial: false,
404 }
405 }
406
407 pub fn generic(generic: GenericFontFamily) -> &'static Self {
409 macro_rules! generic_font_family {
410 ($ident:ident, $family:ident) => {
411 static_font_family!(
412 $ident,
413 SingleFontFamily::Generic(GenericFontFamily::$family)
414 )
415 };
416 }
417
418 generic_font_family!(SERIF, Serif);
419 generic_font_family!(SANS_SERIF, SansSerif);
420 generic_font_family!(MONOSPACE, Monospace);
421 generic_font_family!(CURSIVE, Cursive);
422 generic_font_family!(FANTASY, Fantasy);
423 #[cfg(feature = "gecko")]
424 generic_font_family!(MATH, Math);
425 #[cfg(feature = "gecko")]
426 generic_font_family!(MOZ_EMOJI, MozEmoji);
427 generic_font_family!(SYSTEM_UI, SystemUi);
428
429 let family = match generic {
430 GenericFontFamily::None => {
431 debug_assert!(false, "Bogus caller!");
432 &*SERIF
433 },
434 GenericFontFamily::Serif => &*SERIF,
435 GenericFontFamily::SansSerif => &*SANS_SERIF,
436 GenericFontFamily::Monospace => &*MONOSPACE,
437 GenericFontFamily::Cursive => &*CURSIVE,
438 GenericFontFamily::Fantasy => &*FANTASY,
439 #[cfg(feature = "gecko")]
440 GenericFontFamily::Math => &*MATH,
441 #[cfg(feature = "gecko")]
442 GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
443 GenericFontFamily::SystemUi => &*SYSTEM_UI,
444 };
445 debug_assert_eq!(
446 *family.families.iter().next().unwrap(),
447 SingleFontFamily::Generic(generic)
448 );
449 family
450 }
451}
452
453impl MallocSizeOf for FontFamily {
454 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
455 use malloc_size_of::MallocUnconditionalSizeOf;
456 let shared_font_list = &self.families.list;
460 if shared_font_list.is_unique() {
461 shared_font_list.unconditional_size_of(ops)
462 } else {
463 0
464 }
465 }
466}
467
468impl ToCss for FontFamily {
469 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
470 where
471 W: fmt::Write,
472 {
473 let mut iter = self.families.iter();
474 match iter.next() {
475 Some(f) => f.to_css(dest)?,
476 None => return Ok(()),
477 }
478 for family in iter {
479 dest.write_str(", ")?;
480 family.to_css(dest)?;
481 }
482 Ok(())
483 }
484}
485
486#[derive(
488 Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
489)]
490#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
491#[repr(C)]
492pub struct FamilyName {
493 pub name: Atom,
495 pub syntax: FontFamilyNameSyntax,
497}
498
499#[cfg(feature = "gecko")]
500impl FamilyName {
501 fn is_known_icon_font_family(&self) -> bool {
502 use crate::gecko_bindings::bindings;
503 unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
504 }
505}
506
507#[cfg(feature = "servo")]
508impl FamilyName {
509 fn is_known_icon_font_family(&self) -> bool {
510 false
511 }
512}
513
514impl ToCss for FamilyName {
515 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
516 where
517 W: fmt::Write,
518 {
519 match self.syntax {
520 FontFamilyNameSyntax::Quoted => {
521 dest.write_char('"')?;
522 write!(CssStringWriter::new(dest), "{}", self.name)?;
523 dest.write_char('"')
524 },
525 FontFamilyNameSyntax::Identifiers => {
526 let mut first = true;
527 for ident in self.name.to_string().split(' ') {
528 if first {
529 first = false;
530 } else {
531 dest.write_char(' ')?;
532 }
533 debug_assert!(
534 !ident.is_empty(),
535 "Family name with leading, \
536 trailing, or consecutive white spaces should \
537 have been marked quoted by the parser"
538 );
539 serialize_identifier(ident, dest)?;
540 }
541 Ok(())
542 },
543 }
544 }
545}
546
547#[derive(
548 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
549)]
550#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
551#[repr(u8)]
554pub enum FontFamilyNameSyntax {
555 Quoted,
558
559 Identifiers,
562}
563
564#[derive(
567 Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
568)]
569#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
570#[repr(u8)]
571pub enum SingleFontFamily {
572 FamilyName(FamilyName),
574 Generic(GenericFontFamily),
576}
577
578fn system_ui_enabled(_: &ParserContext) -> bool {
579 static_prefs::pref!("layout.css.system-ui.enabled")
580}
581
582#[cfg(feature = "gecko")]
583fn math_enabled(context: &ParserContext) -> bool {
584 context.chrome_rules_enabled() || static_prefs::pref!("mathml.font_family_math.enabled")
585}
586
587#[derive(
597 Clone,
598 Copy,
599 Debug,
600 Eq,
601 Hash,
602 MallocSizeOf,
603 PartialEq,
604 Parse,
605 ToCss,
606 ToComputedValue,
607 ToResolvedValue,
608 ToShmem,
609)]
610#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
611#[repr(u32)]
612#[allow(missing_docs)]
613pub enum GenericFontFamily {
614 #[css(skip)]
618 None = 0,
619 Serif,
620 SansSerif,
621 #[parse(aliases = "-moz-fixed")]
622 Monospace,
623 Cursive,
624 Fantasy,
625 #[cfg(feature = "gecko")]
626 #[parse(condition = "math_enabled")]
627 Math,
628 #[parse(condition = "system_ui_enabled")]
629 SystemUi,
630 #[css(skip)]
632 #[cfg(feature = "gecko")]
633 MozEmoji,
634}
635
636impl GenericFontFamily {
637 pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
641 match self {
642 Self::None | Self::Cursive | Self::Fantasy | Self::SystemUi => false,
643 #[cfg(feature = "gecko")]
644 Self::Math | Self::MozEmoji => false,
645 Self::Serif | Self::SansSerif | Self::Monospace => true,
646 }
647 }
648}
649
650impl Parse for SingleFontFamily {
651 fn parse<'i, 't>(
653 context: &ParserContext,
654 input: &mut Parser<'i, 't>,
655 ) -> Result<Self, ParseError<'i>> {
656 if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
657 return Ok(SingleFontFamily::FamilyName(FamilyName {
658 name: Atom::from(&*value),
659 syntax: FontFamilyNameSyntax::Quoted,
660 }));
661 }
662
663 if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
664 return Ok(SingleFontFamily::Generic(generic));
665 }
666
667 let first_ident = input.expect_ident_cloned()?;
668 let reserved = match_ignore_ascii_case! { &first_ident,
669 "inherit" | "initial" | "unset" | "revert" | "default" => true,
677 _ => false,
678 };
679
680 let mut value = first_ident.as_ref().to_owned();
681 let mut serialize_quoted = value.contains(' ');
682
683 if reserved {
686 let ident = input.expect_ident()?;
687 serialize_quoted = serialize_quoted || ident.contains(' ');
688 value.push(' ');
689 value.push_str(&ident);
690 }
691 while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
692 serialize_quoted = serialize_quoted || ident.contains(' ');
693 value.push(' ');
694 value.push_str(&ident);
695 }
696 let syntax = if serialize_quoted {
697 FontFamilyNameSyntax::Quoted
702 } else {
703 FontFamilyNameSyntax::Identifiers
704 };
705 Ok(SingleFontFamily::FamilyName(FamilyName {
706 name: Atom::from(value),
707 syntax,
708 }))
709 }
710}
711
712#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
714#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
715#[repr(C)]
716pub struct FontFamilyList {
717 pub list: crate::ArcSlice<SingleFontFamily>,
719}
720
721impl FontFamilyList {
722 pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
724 self.list.iter()
725 }
726
727 #[cfg_attr(feature = "servo", allow(unused))]
734 pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
735 let mut index_of_first_generic = None;
736 let mut target_index = None;
737
738 for (i, f) in self.iter().enumerate() {
739 match &*f {
740 SingleFontFamily::Generic(f) => {
741 if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
742 if target_index.is_none() {
746 return;
747 }
748 index_of_first_generic = Some(i);
749 break;
750 }
751 if target_index.is_none() {
754 target_index = Some(i);
755 }
756 },
757 SingleFontFamily::FamilyName(fam) => {
758 if target_index.is_none() && !fam.is_known_icon_font_family() {
761 target_index = Some(i);
762 }
763 },
764 }
765 }
766
767 let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
768 let first_generic = match index_of_first_generic {
769 Some(i) => new_list.remove(i),
770 None => SingleFontFamily::Generic(generic),
771 };
772
773 if let Some(i) = target_index {
774 new_list.insert(i, first_generic);
775 } else {
776 new_list.push(first_generic);
777 }
778 self.list = crate::ArcSlice::from_iter(new_list.into_iter());
779 }
780
781 #[cfg_attr(feature = "servo", allow(unused))]
783 pub(crate) fn needs_user_font_prioritization(&self) -> bool {
784 self.iter().next().map_or(true, |f| match f {
785 SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
786 _ => true,
787 })
788 }
789
790 pub fn single_generic(&self) -> Option<GenericFontFamily> {
792 let mut iter = self.iter();
793 if let Some(SingleFontFamily::Generic(f)) = iter.next() {
794 if iter.next().is_none() {
795 return Some(*f);
796 }
797 }
798 None
799 }
800}
801
802pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
804
805impl FontSizeAdjust {
806 #[inline]
807 pub fn none() -> Self {
809 FontSizeAdjust::None
810 }
811}
812
813impl ToComputedValue for specified::FontSizeAdjust {
814 type ComputedValue = FontSizeAdjust;
815
816 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
817 use crate::font_metrics::FontMetricsOrientation;
818
819 let font_metrics = |vertical, flags| {
820 let orient = if vertical {
821 FontMetricsOrientation::MatchContextPreferVertical
822 } else {
823 FontMetricsOrientation::Horizontal
824 };
825 let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
826 let font_size = context.style().get_font().clone_font_size().used_size.0;
827 (metrics, font_size)
828 };
829
830 macro_rules! resolve {
834 ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
835 match $value {
836 specified::FontSizeAdjustFactor::Number(f) => {
837 FontSizeAdjust::$basis(f.to_computed_value(context))
838 },
839 specified::FontSizeAdjustFactor::FromFont => {
840 let (metrics, font_size) = font_metrics($vertical, $flags);
841 let ratio = if let Some(metric) = metrics.$field {
842 metric / font_size
843 } else if $fallback >= 0.0 {
844 $fallback
845 } else {
846 metrics.ascent / font_size
847 };
848 if ratio.is_nan() {
849 FontSizeAdjust::$basis(NonNegative(abs($fallback)))
850 } else {
851 FontSizeAdjust::$basis(NonNegative(ratio))
852 }
853 },
854 }
855 }};
856 }
857
858 match *self {
859 Self::None => FontSizeAdjust::None,
860 Self::ExHeight(val) => {
861 resolve!(
862 ExHeight,
863 val,
864 false,
865 x_height,
866 0.5,
867 QueryFontMetricsFlags::empty()
868 )
869 },
870 Self::CapHeight(val) => {
871 resolve!(
872 CapHeight,
873 val,
874 false,
875 cap_height,
876 -1.0, QueryFontMetricsFlags::empty()
878 )
879 },
880 Self::ChWidth(val) => {
881 resolve!(
882 ChWidth,
883 val,
884 false,
885 zero_advance_measure,
886 0.5,
887 QueryFontMetricsFlags::NEEDS_CH
888 )
889 },
890 Self::IcWidth(val) => {
891 resolve!(
892 IcWidth,
893 val,
894 false,
895 ic_width,
896 1.0,
897 QueryFontMetricsFlags::NEEDS_IC
898 )
899 },
900 Self::IcHeight(val) => {
901 resolve!(
902 IcHeight,
903 val,
904 true,
905 ic_width,
906 1.0,
907 QueryFontMetricsFlags::NEEDS_IC
908 )
909 },
910 }
911 }
912
913 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
914 macro_rules! case {
915 ($basis:ident, $val:expr) => {
916 Self::$basis(specified::FontSizeAdjustFactor::Number(
917 ToComputedValue::from_computed_value($val),
918 ))
919 };
920 }
921 match *computed {
922 FontSizeAdjust::None => Self::None,
923 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
924 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
925 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
926 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
927 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
928 }
929 }
930}
931
932pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
934
935pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
937
938fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
941where
942 T: TaggedFontValue,
943{
944 if settings_list.len() > 1 {
945 settings_list.sort_by_key(|k| k.tag().0);
946 let mut prev_tag = settings_list.last().unwrap().tag();
949 for i in (0..settings_list.len() - 1).rev() {
950 let cur_tag = settings_list[i].tag();
951 if cur_tag == prev_tag {
952 settings_list.remove(i);
953 }
954 prev_tag = cur_tag;
955 }
956 }
957}
958
959impl<T> ToComputedValue for FontSettings<T>
960where
961 T: ToComputedValue,
962 <T as ToComputedValue>::ComputedValue: TaggedFontValue,
963{
964 type ComputedValue = FontSettings<T::ComputedValue>;
965
966 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
967 let mut v = self
968 .0
969 .iter()
970 .map(|item| item.to_computed_value(context))
971 .collect::<Vec<_>>();
972 dedup_font_settings(&mut v);
973 FontSettings(v.into_boxed_slice())
974 }
975
976 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
977 Self(computed.0.iter().map(T::from_computed_value).collect())
978 }
979}
980
981#[derive(
986 Clone,
987 Copy,
988 Debug,
989 Eq,
990 MallocSizeOf,
991 PartialEq,
992 SpecifiedValueInfo,
993 ToComputedValue,
994 ToResolvedValue,
995 ToShmem,
996 ToTyped,
997)]
998#[repr(C)]
999#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1000#[value_info(other_values = "normal")]
1001pub struct FontLanguageOverride(pub u32);
1002
1003impl FontLanguageOverride {
1004 #[inline]
1005 pub fn normal() -> FontLanguageOverride {
1007 FontLanguageOverride(0)
1008 }
1009
1010 #[inline]
1012 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
1013 *storage = u32::to_be_bytes(self.0);
1014 let slice = if cfg!(debug_assertions) {
1016 std::str::from_utf8(&storage[..]).unwrap()
1017 } else {
1018 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
1019 };
1020 slice.trim_end()
1021 }
1022
1023 #[inline]
1026 pub unsafe fn from_u32(value: u32) -> Self {
1027 Self(value)
1028 }
1029}
1030
1031impl ToCss for FontLanguageOverride {
1032 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1033 where
1034 W: fmt::Write,
1035 {
1036 if self.0 == 0 {
1037 return dest.write_str("normal");
1038 }
1039 self.to_str(&mut [0; 4]).to_css(dest)
1040 }
1041}
1042
1043impl ToComputedValue for specified::MozScriptMinSize {
1044 type ComputedValue = MozScriptMinSize;
1045
1046 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1047 let base_size = FontBaseSize::InheritedStyle;
1050 let line_height_base = LineHeightBase::InheritedStyle;
1051 match self.0 {
1052 NoCalcLength::FontRelative(value) => {
1053 value.to_computed_value(cx, base_size, line_height_base)
1054 },
1055 NoCalcLength::ServoCharacterWidth(value) => {
1056 value.to_computed_value(base_size.resolve(cx).computed_size())
1057 },
1058 ref l => l.to_computed_value(cx),
1059 }
1060 }
1061
1062 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1063 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1064 }
1065}
1066
1067pub type MathDepth = i8;
1069
1070#[cfg(feature = "gecko")]
1071impl ToComputedValue for specified::MathDepth {
1072 type ComputedValue = MathDepth;
1073
1074 fn to_computed_value(&self, cx: &Context) -> i8 {
1075 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1076 use std::{cmp, i8};
1077
1078 let int = match *self {
1079 specified::MathDepth::AutoAdd => {
1080 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
1081 let style = cx.builder.get_parent_font().clone_math_style();
1082 if style == MathStyleValue::Compact {
1083 parent.saturating_add(1)
1084 } else {
1085 parent
1086 }
1087 },
1088 specified::MathDepth::Add(rel) => {
1089 let parent = cx.builder.get_parent_font().clone_math_depth();
1090 (parent as i32).saturating_add(rel.to_computed_value(cx))
1091 },
1092 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1093 };
1094 cmp::min(int, i8::MAX as i32) as i8
1095 }
1096
1097 fn from_computed_value(other: &i8) -> Self {
1098 let computed_value = *other as i32;
1099 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1100 }
1101}
1102
1103pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1108
1109pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1112
1113#[derive(
1124 Clone,
1125 ComputeSquaredDistance,
1126 Copy,
1127 Debug,
1128 Eq,
1129 Hash,
1130 MallocSizeOf,
1131 PartialEq,
1132 PartialOrd,
1133 ToResolvedValue,
1134 ToTyped,
1135)]
1136#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1137#[repr(C)]
1138pub struct FontStyle(FontStyleFixedPoint);
1139
1140impl FontStyle {
1141 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1143 value: 0 << FONT_STYLE_FRACTION_BITS,
1144 });
1145
1146 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1148 value: 100 << FONT_STYLE_FRACTION_BITS,
1149 });
1150
1151 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1154
1155 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1157 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1158 });
1159
1160 #[inline]
1162 pub fn normal() -> Self {
1163 Self::NORMAL
1164 }
1165
1166 pub fn oblique(degrees: f32) -> Self {
1168 Self(FixedPoint::from_float(
1169 degrees
1170 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1171 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1172 ))
1173 }
1174
1175 pub fn oblique_degrees(&self) -> f32 {
1177 debug_assert_ne!(*self, Self::ITALIC);
1178 self.0.to_float()
1179 }
1180}
1181
1182impl ToCss for FontStyle {
1183 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1184 where
1185 W: fmt::Write,
1186 {
1187 if *self == Self::NORMAL {
1188 return dest.write_str("normal");
1189 }
1190 if *self == Self::ITALIC {
1191 return dest.write_str("italic");
1192 }
1193 dest.write_str("oblique")?;
1194 if *self != Self::OBLIQUE {
1195 dest.write_char(' ')?;
1197 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1198 }
1199 Ok(())
1200 }
1201}
1202
1203impl ToAnimatedValue for FontStyle {
1204 type AnimatedValue = generics::FontStyle<Angle>;
1205
1206 #[inline]
1207 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1208 if self == Self::ITALIC {
1209 return generics::FontStyle::Italic;
1210 }
1211 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1212 }
1213
1214 #[inline]
1215 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1216 match animated {
1217 generics::FontStyle::Italic => Self::ITALIC,
1218 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1219 }
1220 }
1221}
1222
1223pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
1230
1231pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
1234
1235#[derive(
1244 Clone,
1245 ComputeSquaredDistance,
1246 Copy,
1247 Debug,
1248 MallocSizeOf,
1249 PartialEq,
1250 PartialOrd,
1251 ToResolvedValue,
1252 ToTyped,
1253)]
1254#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1255#[repr(C)]
1256pub struct FontStretch(pub FontStretchFixedPoint);
1257
1258impl FontStretch {
1259 pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
1261 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
1263
1264 pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1266 value: 50 << Self::FRACTION_BITS,
1267 });
1268 pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1270 value: (62 << Self::FRACTION_BITS) + Self::HALF,
1271 });
1272 pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1274 value: 75 << Self::FRACTION_BITS,
1275 });
1276 pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1278 value: (87 << Self::FRACTION_BITS) + Self::HALF,
1279 });
1280 pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
1282 value: 100 << Self::FRACTION_BITS,
1283 });
1284 pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1286 value: (112 << Self::FRACTION_BITS) + Self::HALF,
1287 });
1288 pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1290 value: 125 << Self::FRACTION_BITS,
1291 });
1292 pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1294 value: 150 << Self::FRACTION_BITS,
1295 });
1296 pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1298 value: 200 << Self::FRACTION_BITS,
1299 });
1300
1301 pub fn hundred() -> Self {
1303 Self::NORMAL
1304 }
1305
1306 #[inline]
1308 pub fn to_percentage(&self) -> Percentage {
1309 Percentage(self.0.to_float() / 100.0)
1310 }
1311
1312 pub fn from_percentage(p: f32) -> Self {
1314 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
1315 }
1316
1317 pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
1320 use specified::FontStretchKeyword::*;
1321 match kw {
1322 UltraCondensed => Self::ULTRA_CONDENSED,
1323 ExtraCondensed => Self::EXTRA_CONDENSED,
1324 Condensed => Self::CONDENSED,
1325 SemiCondensed => Self::SEMI_CONDENSED,
1326 Normal => Self::NORMAL,
1327 SemiExpanded => Self::SEMI_EXPANDED,
1328 Expanded => Self::EXPANDED,
1329 ExtraExpanded => Self::EXTRA_EXPANDED,
1330 UltraExpanded => Self::ULTRA_EXPANDED,
1331 }
1332 }
1333
1334 pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
1336 use specified::FontStretchKeyword::*;
1337 if *self == Self::ULTRA_CONDENSED {
1339 return Some(UltraCondensed);
1340 }
1341 if *self == Self::EXTRA_CONDENSED {
1342 return Some(ExtraCondensed);
1343 }
1344 if *self == Self::CONDENSED {
1345 return Some(Condensed);
1346 }
1347 if *self == Self::SEMI_CONDENSED {
1348 return Some(SemiCondensed);
1349 }
1350 if *self == Self::NORMAL {
1351 return Some(Normal);
1352 }
1353 if *self == Self::SEMI_EXPANDED {
1354 return Some(SemiExpanded);
1355 }
1356 if *self == Self::EXPANDED {
1357 return Some(Expanded);
1358 }
1359 if *self == Self::EXTRA_EXPANDED {
1360 return Some(ExtraExpanded);
1361 }
1362 if *self == Self::ULTRA_EXPANDED {
1363 return Some(UltraExpanded);
1364 }
1365 None
1366 }
1367}
1368
1369impl ToCss for FontStretch {
1370 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1371 where
1372 W: fmt::Write,
1373 {
1374 self.to_percentage().to_css(dest)
1375 }
1376}
1377
1378impl ToAnimatedValue for FontStretch {
1379 type AnimatedValue = Percentage;
1380
1381 #[inline]
1382 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1383 self.to_percentage()
1384 }
1385
1386 #[inline]
1387 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1388 Self::from_percentage(animated.0)
1389 }
1390}
1391
1392pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1394
1395impl ToResolvedValue for LineHeight {
1396 type ResolvedValue = Self;
1397
1398 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1399 #[cfg(feature = "gecko")]
1400 {
1401 if matches!(self, Self::Normal | Self::MozBlockHeight) {
1403 return self;
1404 }
1405 let wm = context.style.writing_mode;
1406 Self::Length(
1407 context
1408 .device
1409 .calc_line_height(
1410 context.style.get_font(),
1411 wm,
1412 Some(context.element_info.element),
1413 )
1414 .to_resolved_value(context),
1415 )
1416 }
1417 #[cfg(feature = "servo")]
1418 {
1419 if let LineHeight::Number(num) = &self {
1420 let size = context.style.get_font().clone_font_size().computed_size();
1421 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1422 } else {
1423 self
1424 }
1425 }
1426 }
1427
1428 #[inline]
1429 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1430 value
1431 }
1432}