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::values::CSSInteger;
23use crate::Atom;
24use cssparser::{serialize_identifier, CssStringWriter, Parser};
25use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
26use num_traits::abs;
27use num_traits::cast::AsPrimitive;
28use std::fmt::{self, Write};
29use style_traits::{CssWriter, ParseError, ToCss};
30
31pub use crate::values::computed::Length as MozScriptMinSize;
32pub use crate::values::specified::font::MozScriptSizeMultiplier;
33pub use crate::values::specified::font::{FontPalette, FontSynthesis, FontSynthesisStyle};
34pub use crate::values::specified::font::{
35 FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric,
36 QueryFontMetricsFlags, XLang, XTextScale,
37};
38pub use crate::values::specified::Integer as SpecifiedInteger;
39pub use crate::values::specified::Number as SpecifiedNumber;
40
41#[repr(C)]
60#[derive(
61 Clone,
62 ComputeSquaredDistance,
63 Copy,
64 Debug,
65 Eq,
66 Hash,
67 MallocSizeOf,
68 PartialEq,
69 PartialOrd,
70 ToResolvedValue,
71)]
72#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
73pub struct FixedPoint<T, const FRACTION_BITS: u16> {
74 pub value: T,
76}
77
78impl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>
79where
80 T: AsPrimitive<f32>,
81 f32: AsPrimitive<T>,
82 u16: AsPrimitive<T>,
83{
84 const SCALE: u16 = 1 << FRACTION_BITS;
85 const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;
86
87 pub fn from_float(v: f32) -> Self {
89 Self {
90 value: (v * Self::SCALE as f32).round().as_(),
91 }
92 }
93
94 pub fn to_float(&self) -> f32 {
96 self.value.as_() * Self::INVERSE_SCALE
97 }
98}
99
100impl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> {
103 type Output = Self;
104 fn div(self, rhs: Self) -> Self {
105 Self {
106 value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16,
107 }
108 }
109}
110impl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> {
111 type Output = Self;
112 fn mul(self, rhs: Self) -> Self {
113 Self {
114 value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16,
115 }
116 }
117}
118
119pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
124
125pub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;
128
129#[derive(
138 Clone,
139 ComputeSquaredDistance,
140 Copy,
141 Debug,
142 Hash,
143 MallocSizeOf,
144 PartialEq,
145 PartialOrd,
146 ToResolvedValue,
147 ToTyped,
148)]
149#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
150#[repr(C)]
151pub struct FontWeight(FontWeightFixedPoint);
152impl ToAnimatedValue for FontWeight {
153 type AnimatedValue = Number;
154
155 #[inline]
156 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
157 self.value()
158 }
159
160 #[inline]
161 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
162 FontWeight::from_float(animated)
163 }
164}
165
166impl ToCss for FontWeight {
167 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
168 where
169 W: fmt::Write,
170 {
171 self.value().to_css(dest)
172 }
173}
174
175impl FontWeight {
176 pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
178 value: 400 << FONT_WEIGHT_FRACTION_BITS,
179 });
180
181 pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
183 value: 700 << FONT_WEIGHT_FRACTION_BITS,
184 });
185
186 pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
188 value: 600 << FONT_WEIGHT_FRACTION_BITS,
189 });
190
191 pub fn normal() -> Self {
193 Self::NORMAL
194 }
195
196 pub fn is_bold(&self) -> bool {
198 *self >= Self::BOLD_THRESHOLD
199 }
200
201 pub fn value(&self) -> f32 {
203 self.0.to_float()
204 }
205
206 pub fn from_float(v: f32) -> Self {
208 Self(FixedPoint::from_float(
209 v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
210 ))
211 }
212
213 pub fn bolder(self) -> Self {
218 let value = self.value();
219 if value < 350. {
220 return Self::NORMAL;
221 }
222 if value < 550. {
223 return Self::BOLD;
224 }
225 Self::from_float(value.max(900.))
226 }
227
228 pub fn lighter(self) -> Self {
233 let value = self.value();
234 if value < 550. {
235 return Self::from_float(value.min(100.));
236 }
237 if value < 750. {
238 return Self::NORMAL;
239 }
240 Self::BOLD
241 }
242}
243
244#[derive(
245 Animate,
246 Clone,
247 ComputeSquaredDistance,
248 Copy,
249 Debug,
250 MallocSizeOf,
251 PartialEq,
252 ToAnimatedZero,
253 ToCss,
254 ToTyped,
255)]
256#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
257pub struct FontSize {
259 pub computed_size: NonNegativeLength,
262 #[css(skip)]
265 pub used_size: NonNegativeLength,
266 #[css(skip)]
268 pub keyword_info: KeywordInfo,
269}
270
271impl FontSize {
272 #[inline]
274 pub fn computed_size(&self) -> Length {
275 self.computed_size.0
276 }
277
278 #[inline]
280 pub fn used_size(&self) -> Length {
281 self.used_size.0
282 }
283
284 #[inline]
286 pub fn zoom(&self, zoom: Zoom) -> Self {
287 Self {
288 computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
289 used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
290 keyword_info: self.keyword_info,
291 }
292 }
293
294 #[inline]
295 pub fn medium() -> Self {
297 Self {
298 computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
299 used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
300 keyword_info: KeywordInfo::medium(),
301 }
302 }
303}
304
305impl ToAnimatedValue for FontSize {
306 type AnimatedValue = Length;
307
308 #[inline]
309 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
310 self.computed_size.0.to_animated_value(context)
311 }
312
313 #[inline]
314 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
315 FontSize {
316 computed_size: NonNegative(animated.clamp_to_non_negative()),
317 used_size: NonNegative(animated.clamp_to_non_negative()),
318 keyword_info: KeywordInfo::none(),
319 }
320 }
321}
322
323impl ToResolvedValue for FontSize {
324 type ResolvedValue = NonNegativeLength;
325
326 #[inline]
327 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
328 self.computed_size.to_resolved_value(context)
329 }
330
331 #[inline]
332 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
333 let computed_size = NonNegativeLength::from_resolved_value(resolved);
334 Self {
335 computed_size,
336 used_size: computed_size,
337 keyword_info: KeywordInfo::none(),
338 }
339 }
340}
341
342#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue, ToTyped)]
343#[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))]
344#[repr(C)]
346pub struct FontFamily {
347 pub families: FontFamilyList,
349 pub is_system_font: bool,
351 pub is_initial: bool,
354}
355
356macro_rules! static_font_family {
357 ($ident:ident, $family:expr) => {
358 lazy_static! {
359 static ref $ident: FontFamily = FontFamily {
360 families: FontFamilyList {
361 list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
362 },
363 is_system_font: false,
364 is_initial: false,
365 };
366 }
367 };
368}
369
370impl FontFamily {
371 #[inline]
372 pub fn serif() -> Self {
374 Self::generic(GenericFontFamily::Serif).clone()
375 }
376
377 #[cfg(feature = "gecko")]
379 pub(crate) fn moz_bullet() -> &'static Self {
380 static_font_family!(
381 MOZ_BULLET,
382 SingleFontFamily::FamilyName(FamilyName {
383 name: atom!("-moz-bullet-font"),
384 syntax: FontFamilyNameSyntax::Identifiers,
385 })
386 );
387
388 &*MOZ_BULLET
389 }
390
391 #[cfg(feature = "gecko")]
393 pub fn for_system_font(name: &str) -> Self {
394 Self {
395 families: FontFamilyList {
396 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
397 FamilyName {
398 name: Atom::from(name),
399 syntax: FontFamilyNameSyntax::Identifiers,
400 },
401 ))),
402 },
403 is_system_font: true,
404 is_initial: false,
405 }
406 }
407
408 pub fn generic(generic: GenericFontFamily) -> &'static Self {
410 macro_rules! generic_font_family {
411 ($ident:ident, $family:ident) => {
412 static_font_family!(
413 $ident,
414 SingleFontFamily::Generic(GenericFontFamily::$family)
415 )
416 };
417 }
418
419 generic_font_family!(SERIF, Serif);
420 generic_font_family!(SANS_SERIF, SansSerif);
421 generic_font_family!(MONOSPACE, Monospace);
422 generic_font_family!(CURSIVE, Cursive);
423 generic_font_family!(FANTASY, Fantasy);
424 #[cfg(feature = "gecko")]
425 generic_font_family!(MATH, Math);
426 #[cfg(feature = "gecko")]
427 generic_font_family!(MOZ_EMOJI, MozEmoji);
428 generic_font_family!(SYSTEM_UI, SystemUi);
429
430 let family = match generic {
431 GenericFontFamily::None => {
432 debug_assert!(false, "Bogus caller!");
433 &*SERIF
434 },
435 GenericFontFamily::Serif => &*SERIF,
436 GenericFontFamily::SansSerif => &*SANS_SERIF,
437 GenericFontFamily::Monospace => &*MONOSPACE,
438 GenericFontFamily::Cursive => &*CURSIVE,
439 GenericFontFamily::Fantasy => &*FANTASY,
440 #[cfg(feature = "gecko")]
441 GenericFontFamily::Math => &*MATH,
442 #[cfg(feature = "gecko")]
443 GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
444 GenericFontFamily::SystemUi => &*SYSTEM_UI,
445 };
446 debug_assert_eq!(
447 *family.families.iter().next().unwrap(),
448 SingleFontFamily::Generic(generic)
449 );
450 family
451 }
452}
453
454impl MallocSizeOf for FontFamily {
455 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
456 use malloc_size_of::MallocUnconditionalSizeOf;
457 let shared_font_list = &self.families.list;
461 if shared_font_list.is_unique() {
462 shared_font_list.unconditional_size_of(ops)
463 } else {
464 0
465 }
466 }
467}
468
469impl ToCss for FontFamily {
470 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
471 where
472 W: fmt::Write,
473 {
474 let mut iter = self.families.iter();
475 match iter.next() {
476 Some(f) => f.to_css(dest)?,
477 None => return Ok(()),
478 }
479 for family in iter {
480 dest.write_str(", ")?;
481 family.to_css(dest)?;
482 }
483 Ok(())
484 }
485}
486
487#[derive(
489 Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
490)]
491#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
492#[repr(C)]
493pub struct FamilyName {
494 pub name: Atom,
496 pub syntax: FontFamilyNameSyntax,
498}
499
500#[cfg(feature = "gecko")]
501impl FamilyName {
502 fn is_known_icon_font_family(&self) -> bool {
503 use crate::gecko_bindings::bindings;
504 unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
505 }
506}
507
508#[cfg(feature = "servo")]
509impl FamilyName {
510 fn is_known_icon_font_family(&self) -> bool {
511 false
512 }
513}
514
515impl ToCss for FamilyName {
516 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
517 where
518 W: fmt::Write,
519 {
520 match self.syntax {
521 FontFamilyNameSyntax::Quoted => {
522 dest.write_char('"')?;
523 write!(CssStringWriter::new(dest), "{}", self.name)?;
524 dest.write_char('"')
525 },
526 FontFamilyNameSyntax::Identifiers => {
527 let mut first = true;
528 for ident in self.name.to_string().split(' ') {
529 if first {
530 first = false;
531 } else {
532 dest.write_char(' ')?;
533 }
534 debug_assert!(
535 !ident.is_empty(),
536 "Family name with leading, \
537 trailing, or consecutive white spaces should \
538 have been marked quoted by the parser"
539 );
540 serialize_identifier(ident, dest)?;
541 }
542 Ok(())
543 },
544 }
545 }
546}
547
548#[derive(
549 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
550)]
551#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
552#[repr(u8)]
555pub enum FontFamilyNameSyntax {
556 Quoted,
559
560 Identifiers,
563}
564
565#[derive(
568 Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
569)]
570#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
571#[repr(u8)]
572pub enum SingleFontFamily {
573 FamilyName(FamilyName),
575 Generic(GenericFontFamily),
577}
578
579fn system_ui_enabled(_: &ParserContext) -> bool {
580 static_prefs::pref!("layout.css.system-ui.enabled")
581}
582
583#[cfg(feature = "gecko")]
584fn math_enabled(context: &ParserContext) -> bool {
585 context.chrome_rules_enabled() || static_prefs::pref!("mathml.font_family_math.enabled")
586}
587
588#[derive(
598 Clone,
599 Copy,
600 Debug,
601 Eq,
602 Hash,
603 MallocSizeOf,
604 PartialEq,
605 Parse,
606 ToCss,
607 ToComputedValue,
608 ToResolvedValue,
609 ToShmem,
610)]
611#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
612#[repr(u32)]
613#[allow(missing_docs)]
614pub enum GenericFontFamily {
615 #[css(skip)]
619 None = 0,
620 Serif,
621 SansSerif,
622 #[parse(aliases = "-moz-fixed")]
623 Monospace,
624 Cursive,
625 Fantasy,
626 #[cfg(feature = "gecko")]
627 #[parse(condition = "math_enabled")]
628 Math,
629 #[parse(condition = "system_ui_enabled")]
630 SystemUi,
631 #[css(skip)]
633 #[cfg(feature = "gecko")]
634 MozEmoji,
635}
636
637impl GenericFontFamily {
638 pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
642 match self {
643 Self::None | Self::Cursive | Self::Fantasy | Self::SystemUi => false,
644 #[cfg(feature = "gecko")]
645 Self::Math | Self::MozEmoji => false,
646 Self::Serif | Self::SansSerif | Self::Monospace => true,
647 }
648 }
649}
650
651impl Parse for SingleFontFamily {
652 fn parse<'i, 't>(
654 context: &ParserContext,
655 input: &mut Parser<'i, 't>,
656 ) -> Result<Self, ParseError<'i>> {
657 if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
658 return Ok(SingleFontFamily::FamilyName(FamilyName {
659 name: Atom::from(&*value),
660 syntax: FontFamilyNameSyntax::Quoted,
661 }));
662 }
663
664 if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
665 return Ok(SingleFontFamily::Generic(generic));
666 }
667
668 let first_ident = input.expect_ident_cloned()?;
669 let reserved = match_ignore_ascii_case! { &first_ident,
670 "inherit" | "initial" | "unset" | "revert" | "default" => true,
678 _ => false,
679 };
680
681 let mut value = first_ident.as_ref().to_owned();
682 let mut serialize_quoted = value.contains(' ');
683
684 if reserved {
687 let ident = input.expect_ident()?;
688 serialize_quoted = serialize_quoted || ident.contains(' ');
689 value.push(' ');
690 value.push_str(&ident);
691 }
692 while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
693 serialize_quoted = serialize_quoted || ident.contains(' ');
694 value.push(' ');
695 value.push_str(&ident);
696 }
697 let syntax = if serialize_quoted {
698 FontFamilyNameSyntax::Quoted
703 } else {
704 FontFamilyNameSyntax::Identifiers
705 };
706 Ok(SingleFontFamily::FamilyName(FamilyName {
707 name: Atom::from(value),
708 syntax,
709 }))
710 }
711}
712
713#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
715#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
716#[repr(C)]
717pub struct FontFamilyList {
718 pub list: crate::ArcSlice<SingleFontFamily>,
720}
721
722impl FontFamilyList {
723 pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
725 self.list.iter()
726 }
727
728 #[cfg_attr(feature = "servo", allow(unused))]
735 pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
736 let mut index_of_first_generic = None;
737 let mut target_index = None;
738
739 for (i, f) in self.iter().enumerate() {
740 match &*f {
741 SingleFontFamily::Generic(f) => {
742 if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
743 if target_index.is_none() {
747 return;
748 }
749 index_of_first_generic = Some(i);
750 break;
751 }
752 if target_index.is_none() {
755 target_index = Some(i);
756 }
757 },
758 SingleFontFamily::FamilyName(fam) => {
759 if target_index.is_none() && !fam.is_known_icon_font_family() {
762 target_index = Some(i);
763 }
764 },
765 }
766 }
767
768 let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
769 let first_generic = match index_of_first_generic {
770 Some(i) => new_list.remove(i),
771 None => SingleFontFamily::Generic(generic),
772 };
773
774 if let Some(i) = target_index {
775 new_list.insert(i, first_generic);
776 } else {
777 new_list.push(first_generic);
778 }
779 self.list = crate::ArcSlice::from_iter(new_list.into_iter());
780 }
781
782 #[cfg_attr(feature = "servo", allow(unused))]
784 pub(crate) fn needs_user_font_prioritization(&self) -> bool {
785 self.iter().next().map_or(true, |f| match f {
786 SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
787 _ => true,
788 })
789 }
790
791 pub fn single_generic(&self) -> Option<GenericFontFamily> {
793 let mut iter = self.iter();
794 if let Some(SingleFontFamily::Generic(f)) = iter.next() {
795 if iter.next().is_none() {
796 return Some(*f);
797 }
798 }
799 None
800 }
801}
802
803pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
805
806impl FontSizeAdjust {
807 #[inline]
808 pub fn none() -> Self {
810 FontSizeAdjust::None
811 }
812}
813
814impl ToComputedValue for specified::FontSizeAdjust {
815 type ComputedValue = FontSizeAdjust;
816
817 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
818 use crate::font_metrics::FontMetricsOrientation;
819
820 let font_metrics = |vertical, flags| {
821 let orient = if vertical {
822 FontMetricsOrientation::MatchContextPreferVertical
823 } else {
824 FontMetricsOrientation::Horizontal
825 };
826 let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
827 let font_size = context.style().get_font().clone_font_size().used_size.0;
828 (metrics, font_size)
829 };
830
831 macro_rules! resolve {
835 ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
836 match $value {
837 specified::FontSizeAdjustFactor::Number(f) => {
838 FontSizeAdjust::$basis(f.to_computed_value(context))
839 },
840 specified::FontSizeAdjustFactor::FromFont => {
841 let (metrics, font_size) = font_metrics($vertical, $flags);
842 let ratio = if let Some(metric) = metrics.$field {
843 metric / font_size
844 } else if $fallback >= 0.0 {
845 $fallback
846 } else {
847 metrics.ascent / font_size
848 };
849 if ratio.is_nan() {
850 FontSizeAdjust::$basis(NonNegative(abs($fallback)))
851 } else {
852 FontSizeAdjust::$basis(NonNegative(ratio))
853 }
854 },
855 }
856 }};
857 }
858
859 match *self {
860 Self::None => FontSizeAdjust::None,
861 Self::ExHeight(val) => {
862 resolve!(
863 ExHeight,
864 val,
865 false,
866 x_height,
867 0.5,
868 QueryFontMetricsFlags::empty()
869 )
870 },
871 Self::CapHeight(val) => {
872 resolve!(
873 CapHeight,
874 val,
875 false,
876 cap_height,
877 -1.0, QueryFontMetricsFlags::empty()
879 )
880 },
881 Self::ChWidth(val) => {
882 resolve!(
883 ChWidth,
884 val,
885 false,
886 zero_advance_measure,
887 0.5,
888 QueryFontMetricsFlags::NEEDS_CH
889 )
890 },
891 Self::IcWidth(val) => {
892 resolve!(
893 IcWidth,
894 val,
895 false,
896 ic_width,
897 1.0,
898 QueryFontMetricsFlags::NEEDS_IC
899 )
900 },
901 Self::IcHeight(val) => {
902 resolve!(
903 IcHeight,
904 val,
905 true,
906 ic_width,
907 1.0,
908 QueryFontMetricsFlags::NEEDS_IC
909 )
910 },
911 }
912 }
913
914 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
915 macro_rules! case {
916 ($basis:ident, $val:expr) => {
917 Self::$basis(specified::FontSizeAdjustFactor::Number(
918 ToComputedValue::from_computed_value($val),
919 ))
920 };
921 }
922 match *computed {
923 FontSizeAdjust::None => Self::None,
924 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
925 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
926 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
927 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
928 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
929 }
930 }
931}
932
933pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
935
936pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
938
939fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
942where
943 T: TaggedFontValue,
944{
945 if settings_list.len() > 1 {
946 settings_list.sort_by_key(|k| k.tag().0);
947 let mut prev_tag = settings_list.last().unwrap().tag();
950 for i in (0..settings_list.len() - 1).rev() {
951 let cur_tag = settings_list[i].tag();
952 if cur_tag == prev_tag {
953 settings_list.remove(i);
954 }
955 prev_tag = cur_tag;
956 }
957 }
958}
959
960impl<T> ToComputedValue for FontSettings<T>
961where
962 T: ToComputedValue,
963 <T as ToComputedValue>::ComputedValue: TaggedFontValue,
964{
965 type ComputedValue = FontSettings<T::ComputedValue>;
966
967 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
968 let mut v = self
969 .0
970 .iter()
971 .map(|item| item.to_computed_value(context))
972 .collect::<Vec<_>>();
973 dedup_font_settings(&mut v);
974 FontSettings(v.into_boxed_slice())
975 }
976
977 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
978 Self(computed.0.iter().map(T::from_computed_value).collect())
979 }
980}
981
982#[derive(
987 Clone,
988 Copy,
989 Debug,
990 Eq,
991 MallocSizeOf,
992 PartialEq,
993 SpecifiedValueInfo,
994 ToComputedValue,
995 ToResolvedValue,
996 ToShmem,
997 ToTyped,
998)]
999#[repr(C)]
1000#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1001#[value_info(other_values = "normal")]
1002pub struct FontLanguageOverride(pub u32);
1003
1004impl FontLanguageOverride {
1005 #[inline]
1006 pub fn normal() -> FontLanguageOverride {
1008 FontLanguageOverride(0)
1009 }
1010
1011 #[inline]
1013 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
1014 *storage = u32::to_be_bytes(self.0);
1015 let slice = if cfg!(debug_assertions) {
1017 std::str::from_utf8(&storage[..]).unwrap()
1018 } else {
1019 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
1020 };
1021 slice.trim_end()
1022 }
1023
1024 #[inline]
1027 pub unsafe fn from_u32(value: u32) -> Self {
1028 Self(value)
1029 }
1030}
1031
1032impl ToCss for FontLanguageOverride {
1033 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1034 where
1035 W: fmt::Write,
1036 {
1037 if self.0 == 0 {
1038 return dest.write_str("normal");
1039 }
1040 self.to_str(&mut [0; 4]).to_css(dest)
1041 }
1042}
1043
1044impl ToComputedValue for specified::MozScriptMinSize {
1045 type ComputedValue = MozScriptMinSize;
1046
1047 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1048 let base_size = FontBaseSize::InheritedStyle;
1051 let line_height_base = LineHeightBase::InheritedStyle;
1052 match self.0 {
1053 NoCalcLength::FontRelative(value) => {
1054 value.to_computed_value(cx, base_size, line_height_base)
1055 },
1056 NoCalcLength::ServoCharacterWidth(value) => {
1057 value.to_computed_value(base_size.resolve(cx).computed_size())
1058 },
1059 ref l => l.to_computed_value(cx),
1060 }
1061 }
1062
1063 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1064 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1065 }
1066}
1067
1068pub type MathDepth = i8;
1070
1071#[cfg(feature = "gecko")]
1072impl ToComputedValue for specified::MathDepth {
1073 type ComputedValue = MathDepth;
1074
1075 fn to_computed_value(&self, cx: &Context) -> i8 {
1076 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1077 use std::{cmp, i8};
1078
1079 let int = match *self {
1080 specified::MathDepth::AutoAdd => {
1081 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
1082 let style = cx.builder.get_parent_font().clone_math_style();
1083 if style == MathStyleValue::Compact {
1084 parent.saturating_add(1)
1085 } else {
1086 parent
1087 }
1088 },
1089 specified::MathDepth::Add(rel) => {
1090 let parent = cx.builder.get_parent_font().clone_math_depth();
1091 (parent as i32).saturating_add(rel.to_computed_value(cx))
1092 },
1093 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1094 };
1095 cmp::min(int, i8::MAX as i32) as i8
1096 }
1097
1098 fn from_computed_value(other: &i8) -> Self {
1099 let computed_value = *other as i32;
1100 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1101 }
1102}
1103
1104impl ToAnimatedValue for MathDepth {
1105 type AnimatedValue = CSSInteger;
1106
1107 #[inline]
1108 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1109 self.into()
1110 }
1111
1112 #[inline]
1113 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1114 use std::{cmp, i8};
1115 cmp::min(animated, i8::MAX as i32) as i8
1116 }
1117}
1118
1119pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1124
1125pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1128
1129#[derive(
1140 Clone,
1141 ComputeSquaredDistance,
1142 Copy,
1143 Debug,
1144 Eq,
1145 Hash,
1146 MallocSizeOf,
1147 PartialEq,
1148 PartialOrd,
1149 ToResolvedValue,
1150 ToTyped,
1151)]
1152#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1153#[repr(C)]
1154pub struct FontStyle(FontStyleFixedPoint);
1155
1156impl FontStyle {
1157 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1159 value: 0 << FONT_STYLE_FRACTION_BITS,
1160 });
1161
1162 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1164 value: 100 << FONT_STYLE_FRACTION_BITS,
1165 });
1166
1167 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1170
1171 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1173 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1174 });
1175
1176 #[inline]
1178 pub fn normal() -> Self {
1179 Self::NORMAL
1180 }
1181
1182 pub fn oblique(degrees: f32) -> Self {
1184 Self(FixedPoint::from_float(
1185 degrees
1186 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1187 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1188 ))
1189 }
1190
1191 pub fn oblique_degrees(&self) -> f32 {
1193 debug_assert_ne!(*self, Self::ITALIC);
1194 self.0.to_float()
1195 }
1196}
1197
1198impl ToCss for FontStyle {
1199 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1200 where
1201 W: fmt::Write,
1202 {
1203 if *self == Self::NORMAL {
1204 return dest.write_str("normal");
1205 }
1206 if *self == Self::ITALIC {
1207 return dest.write_str("italic");
1208 }
1209 dest.write_str("oblique")?;
1210 if *self != Self::OBLIQUE {
1211 dest.write_char(' ')?;
1213 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1214 }
1215 Ok(())
1216 }
1217}
1218
1219impl ToAnimatedValue for FontStyle {
1220 type AnimatedValue = generics::FontStyle<Angle>;
1221
1222 #[inline]
1223 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1224 if self == Self::ITALIC {
1225 return generics::FontStyle::Italic;
1226 }
1227 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1228 }
1229
1230 #[inline]
1231 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1232 match animated {
1233 generics::FontStyle::Italic => Self::ITALIC,
1234 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1235 }
1236 }
1237}
1238
1239pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
1246
1247pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
1250
1251#[derive(
1260 Clone,
1261 ComputeSquaredDistance,
1262 Copy,
1263 Debug,
1264 MallocSizeOf,
1265 PartialEq,
1266 PartialOrd,
1267 ToResolvedValue,
1268 ToTyped,
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 ToAnimatedValue for FontStretch {
1395 type AnimatedValue = Percentage;
1396
1397 #[inline]
1398 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1399 self.to_percentage()
1400 }
1401
1402 #[inline]
1403 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1404 Self::from_percentage(animated.0)
1405 }
1406}
1407
1408pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1410
1411impl ToResolvedValue for LineHeight {
1412 type ResolvedValue = Self;
1413
1414 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1415 #[cfg(feature = "gecko")]
1416 {
1417 if matches!(self, Self::Normal | Self::MozBlockHeight) {
1419 return self;
1420 }
1421 let wm = context.style.writing_mode;
1422 Self::Length(
1423 context
1424 .device
1425 .calc_line_height(
1426 context.style.get_font(),
1427 wm,
1428 Some(context.element_info.element),
1429 )
1430 .to_resolved_value(context),
1431 )
1432 }
1433 #[cfg(feature = "servo")]
1434 {
1435 if let LineHeight::Number(num) = &self {
1436 let size = context.style.get_font().clone_font_size().computed_size();
1437 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1438 } else {
1439 self
1440 }
1441 }
1442 }
1443
1444 #[inline]
1445 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1446 value
1447 }
1448}