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)]
147#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
148#[repr(C)]
149pub struct FontWeight(FontWeightFixedPoint);
150impl ToAnimatedValue for FontWeight {
151 type AnimatedValue = Number;
152
153 #[inline]
154 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
155 self.value()
156 }
157
158 #[inline]
159 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
160 FontWeight::from_float(animated)
161 }
162}
163
164impl ToCss for FontWeight {
165 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
166 where
167 W: fmt::Write,
168 {
169 self.value().to_css(dest)
170 }
171}
172
173impl FontWeight {
174 pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {
176 value: 400 << FONT_WEIGHT_FRACTION_BITS,
177 });
178
179 pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {
181 value: 700 << FONT_WEIGHT_FRACTION_BITS,
182 });
183
184 pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {
186 value: 600 << FONT_WEIGHT_FRACTION_BITS,
187 });
188
189 pub fn normal() -> Self {
191 Self::NORMAL
192 }
193
194 pub fn is_bold(&self) -> bool {
196 *self >= Self::BOLD_THRESHOLD
197 }
198
199 pub fn value(&self) -> f32 {
201 self.0.to_float()
202 }
203
204 pub fn from_float(v: f32) -> Self {
206 Self(FixedPoint::from_float(
207 v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),
208 ))
209 }
210
211 pub fn bolder(self) -> Self {
216 let value = self.value();
217 if value < 350. {
218 return Self::NORMAL;
219 }
220 if value < 550. {
221 return Self::BOLD;
222 }
223 Self::from_float(value.max(900.))
224 }
225
226 pub fn lighter(self) -> Self {
231 let value = self.value();
232 if value < 550. {
233 return Self::from_float(value.min(100.));
234 }
235 if value < 750. {
236 return Self::NORMAL;
237 }
238 Self::BOLD
239 }
240}
241
242#[derive(
243 Animate,
244 Clone,
245 ComputeSquaredDistance,
246 Copy,
247 Debug,
248 MallocSizeOf,
249 PartialEq,
250 ToAnimatedZero,
251 ToCss,
252)]
253#[cfg_attr(feature = "servo", derive(Serialize, Deserialize))]
254pub struct FontSize {
256 pub computed_size: NonNegativeLength,
259 #[css(skip)]
262 pub used_size: NonNegativeLength,
263 #[css(skip)]
265 pub keyword_info: KeywordInfo,
266}
267
268impl FontSize {
269 #[inline]
271 pub fn computed_size(&self) -> Length {
272 self.computed_size.0
273 }
274
275 #[inline]
277 pub fn used_size(&self) -> Length {
278 self.used_size.0
279 }
280
281 #[inline]
283 pub fn zoom(&self, zoom: Zoom) -> Self {
284 Self {
285 computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
286 used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
287 keyword_info: self.keyword_info,
288 }
289 }
290
291 #[inline]
292 pub fn medium() -> Self {
294 Self {
295 computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
296 used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),
297 keyword_info: KeywordInfo::medium(),
298 }
299 }
300}
301
302impl ToAnimatedValue for FontSize {
303 type AnimatedValue = Length;
304
305 #[inline]
306 fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {
307 self.computed_size.0.to_animated_value(context)
308 }
309
310 #[inline]
311 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
312 FontSize {
313 computed_size: NonNegative(animated.clamp_to_non_negative()),
314 used_size: NonNegative(animated.clamp_to_non_negative()),
315 keyword_info: KeywordInfo::none(),
316 }
317 }
318}
319
320impl ToResolvedValue for FontSize {
321 type ResolvedValue = NonNegativeLength;
322
323 #[inline]
324 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
325 self.computed_size.to_resolved_value(context)
326 }
327
328 #[inline]
329 fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {
330 let computed_size = NonNegativeLength::from_resolved_value(resolved);
331 Self {
332 computed_size,
333 used_size: computed_size,
334 keyword_info: KeywordInfo::none(),
335 }
336 }
337}
338
339#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
340#[cfg_attr(feature = "servo", derive(Hash, Serialize, Deserialize))]
341#[repr(C)]
343pub struct FontFamily {
344 pub families: FontFamilyList,
346 pub is_system_font: bool,
348 pub is_initial: bool,
351}
352
353macro_rules! static_font_family {
354 ($ident:ident, $family:expr) => {
355 lazy_static! {
356 static ref $ident: FontFamily = FontFamily {
357 families: FontFamilyList {
358 list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
359 },
360 is_system_font: false,
361 is_initial: false,
362 };
363 }
364 };
365}
366
367impl FontFamily {
368 #[inline]
369 pub fn serif() -> Self {
371 Self::generic(GenericFontFamily::Serif).clone()
372 }
373
374 #[cfg(feature = "gecko")]
376 pub(crate) fn moz_bullet() -> &'static Self {
377 static_font_family!(
378 MOZ_BULLET,
379 SingleFontFamily::FamilyName(FamilyName {
380 name: atom!("-moz-bullet-font"),
381 syntax: FontFamilyNameSyntax::Identifiers,
382 })
383 );
384
385 &*MOZ_BULLET
386 }
387
388 #[cfg(feature = "gecko")]
390 pub fn for_system_font(name: &str) -> Self {
391 Self {
392 families: FontFamilyList {
393 list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
394 FamilyName {
395 name: Atom::from(name),
396 syntax: FontFamilyNameSyntax::Identifiers,
397 },
398 ))),
399 },
400 is_system_font: true,
401 is_initial: false,
402 }
403 }
404
405 pub fn generic(generic: GenericFontFamily) -> &'static Self {
407 macro_rules! generic_font_family {
408 ($ident:ident, $family:ident) => {
409 static_font_family!(
410 $ident,
411 SingleFontFamily::Generic(GenericFontFamily::$family)
412 )
413 };
414 }
415
416 generic_font_family!(SERIF, Serif);
417 generic_font_family!(SANS_SERIF, SansSerif);
418 generic_font_family!(MONOSPACE, Monospace);
419 generic_font_family!(CURSIVE, Cursive);
420 generic_font_family!(FANTASY, Fantasy);
421 #[cfg(feature = "gecko")]
422 generic_font_family!(MOZ_EMOJI, MozEmoji);
423 generic_font_family!(SYSTEM_UI, SystemUi);
424
425 let family = match generic {
426 GenericFontFamily::None => {
427 debug_assert!(false, "Bogus caller!");
428 &*SERIF
429 },
430 GenericFontFamily::Serif => &*SERIF,
431 GenericFontFamily::SansSerif => &*SANS_SERIF,
432 GenericFontFamily::Monospace => &*MONOSPACE,
433 GenericFontFamily::Cursive => &*CURSIVE,
434 GenericFontFamily::Fantasy => &*FANTASY,
435 #[cfg(feature = "gecko")]
436 GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
437 GenericFontFamily::SystemUi => &*SYSTEM_UI,
438 };
439 debug_assert_eq!(
440 *family.families.iter().next().unwrap(),
441 SingleFontFamily::Generic(generic)
442 );
443 family
444 }
445}
446
447impl MallocSizeOf for FontFamily {
448 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
449 use malloc_size_of::MallocUnconditionalSizeOf;
450 let shared_font_list = &self.families.list;
454 if shared_font_list.is_unique() {
455 shared_font_list.unconditional_size_of(ops)
456 } else {
457 0
458 }
459 }
460}
461
462impl ToCss for FontFamily {
463 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
464 where
465 W: fmt::Write,
466 {
467 let mut iter = self.families.iter();
468 match iter.next() {
469 Some(f) => f.to_css(dest)?,
470 None => return Ok(()),
471 }
472 for family in iter {
473 dest.write_str(", ")?;
474 family.to_css(dest)?;
475 }
476 Ok(())
477 }
478}
479
480#[derive(
482 Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
483)]
484#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
485#[repr(C)]
486pub struct FamilyName {
487 pub name: Atom,
489 pub syntax: FontFamilyNameSyntax,
491}
492
493#[cfg(feature = "gecko")]
494impl FamilyName {
495 fn is_known_icon_font_family(&self) -> bool {
496 use crate::gecko_bindings::bindings;
497 unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }
498 }
499}
500
501#[cfg(feature = "servo")]
502impl FamilyName {
503 fn is_known_icon_font_family(&self) -> bool {
504 false
505 }
506}
507
508impl ToCss for FamilyName {
509 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
510 where
511 W: fmt::Write,
512 {
513 match self.syntax {
514 FontFamilyNameSyntax::Quoted => {
515 dest.write_char('"')?;
516 write!(CssStringWriter::new(dest), "{}", self.name)?;
517 dest.write_char('"')
518 },
519 FontFamilyNameSyntax::Identifiers => {
520 let mut first = true;
521 for ident in self.name.to_string().split(' ') {
522 if first {
523 first = false;
524 } else {
525 dest.write_char(' ')?;
526 }
527 debug_assert!(
528 !ident.is_empty(),
529 "Family name with leading, \
530 trailing, or consecutive white spaces should \
531 have been marked quoted by the parser"
532 );
533 serialize_identifier(ident, dest)?;
534 }
535 Ok(())
536 },
537 }
538 }
539}
540
541#[derive(
542 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
543)]
544#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
545#[repr(u8)]
548pub enum FontFamilyNameSyntax {
549 Quoted,
552
553 Identifiers,
556}
557
558#[derive(
561 Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
562)]
563#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
564#[repr(u8)]
565pub enum SingleFontFamily {
566 FamilyName(FamilyName),
568 Generic(GenericFontFamily),
570}
571
572fn system_ui_enabled(_: &ParserContext) -> bool {
573 static_prefs::pref!("layout.css.system-ui.enabled")
574}
575
576#[derive(
586 Clone,
587 Copy,
588 Debug,
589 Eq,
590 Hash,
591 MallocSizeOf,
592 PartialEq,
593 Parse,
594 ToCss,
595 ToComputedValue,
596 ToResolvedValue,
597 ToShmem,
598)]
599#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
600#[repr(u32)]
601#[allow(missing_docs)]
602pub enum GenericFontFamily {
603 #[css(skip)]
607 None = 0,
608 Serif,
609 SansSerif,
610 #[parse(aliases = "-moz-fixed")]
611 Monospace,
612 Cursive,
613 Fantasy,
614 #[parse(condition = "system_ui_enabled")]
615 SystemUi,
616 #[css(skip)]
618 #[cfg(feature = "gecko")]
619 MozEmoji,
620}
621
622impl GenericFontFamily {
623 pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
627 match self {
628 Self::None | Self::Fantasy | Self::Cursive | Self::SystemUi => false,
629 #[cfg(feature = "gecko")]
630 Self::MozEmoji => false,
631 Self::Serif | Self::SansSerif | Self::Monospace => true,
632 }
633 }
634}
635
636impl Parse for SingleFontFamily {
637 fn parse<'i, 't>(
639 context: &ParserContext,
640 input: &mut Parser<'i, 't>,
641 ) -> Result<Self, ParseError<'i>> {
642 if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
643 return Ok(SingleFontFamily::FamilyName(FamilyName {
644 name: Atom::from(&*value),
645 syntax: FontFamilyNameSyntax::Quoted,
646 }));
647 }
648
649 if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
650 return Ok(SingleFontFamily::Generic(generic));
651 }
652
653 let first_ident = input.expect_ident_cloned()?;
654 let reserved = match_ignore_ascii_case! { &first_ident,
655 "inherit" | "initial" | "unset" | "revert" | "default" => true,
663 _ => false,
664 };
665
666 let mut value = first_ident.as_ref().to_owned();
667 let mut serialize_quoted = value.contains(' ');
668
669 if reserved {
672 let ident = input.expect_ident()?;
673 serialize_quoted = serialize_quoted || ident.contains(' ');
674 value.push(' ');
675 value.push_str(&ident);
676 }
677 while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
678 serialize_quoted = serialize_quoted || ident.contains(' ');
679 value.push(' ');
680 value.push_str(&ident);
681 }
682 let syntax = if serialize_quoted {
683 FontFamilyNameSyntax::Quoted
688 } else {
689 FontFamilyNameSyntax::Identifiers
690 };
691 Ok(SingleFontFamily::FamilyName(FamilyName {
692 name: Atom::from(value),
693 syntax,
694 }))
695 }
696}
697
698#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
700#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
701#[repr(C)]
702pub struct FontFamilyList {
703 pub list: crate::ArcSlice<SingleFontFamily>,
705}
706
707impl FontFamilyList {
708 pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
710 self.list.iter()
711 }
712
713 #[cfg_attr(feature = "servo", allow(unused))]
720 pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
721 let mut index_of_first_generic = None;
722 let mut target_index = None;
723
724 for (i, f) in self.iter().enumerate() {
725 match &*f {
726 SingleFontFamily::Generic(f) => {
727 if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {
728 if target_index.is_none() {
732 return;
733 }
734 index_of_first_generic = Some(i);
735 break;
736 }
737 if target_index.is_none() {
740 target_index = Some(i);
741 }
742 },
743 SingleFontFamily::FamilyName(fam) => {
744 if target_index.is_none() && !fam.is_known_icon_font_family() {
747 target_index = Some(i);
748 }
749 },
750 }
751 }
752
753 let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
754 let first_generic = match index_of_first_generic {
755 Some(i) => new_list.remove(i),
756 None => SingleFontFamily::Generic(generic),
757 };
758
759 if let Some(i) = target_index {
760 new_list.insert(i, first_generic);
761 } else {
762 new_list.push(first_generic);
763 }
764 self.list = crate::ArcSlice::from_iter(new_list.into_iter());
765 }
766
767 #[cfg_attr(feature = "servo", allow(unused))]
769 pub(crate) fn needs_user_font_prioritization(&self) -> bool {
770 self.iter().next().map_or(true, |f| match f {
771 SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
772 _ => true,
773 })
774 }
775
776 pub fn single_generic(&self) -> Option<GenericFontFamily> {
778 let mut iter = self.iter();
779 if let Some(SingleFontFamily::Generic(f)) = iter.next() {
780 if iter.next().is_none() {
781 return Some(*f);
782 }
783 }
784 None
785 }
786}
787
788pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
790
791impl FontSizeAdjust {
792 #[inline]
793 pub fn none() -> Self {
795 FontSizeAdjust::None
796 }
797}
798
799impl ToComputedValue for specified::FontSizeAdjust {
800 type ComputedValue = FontSizeAdjust;
801
802 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
803 use crate::font_metrics::FontMetricsOrientation;
804
805 let font_metrics = |vertical, flags| {
806 let orient = if vertical {
807 FontMetricsOrientation::MatchContextPreferVertical
808 } else {
809 FontMetricsOrientation::Horizontal
810 };
811 let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);
812 let font_size = context.style().get_font().clone_font_size().used_size.0;
813 (metrics, font_size)
814 };
815
816 macro_rules! resolve {
820 ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{
821 match $value {
822 specified::FontSizeAdjustFactor::Number(f) => {
823 FontSizeAdjust::$basis(f.to_computed_value(context))
824 },
825 specified::FontSizeAdjustFactor::FromFont => {
826 let (metrics, font_size) = font_metrics($vertical, $flags);
827 let ratio = if let Some(metric) = metrics.$field {
828 metric / font_size
829 } else if $fallback >= 0.0 {
830 $fallback
831 } else {
832 metrics.ascent / font_size
833 };
834 if ratio.is_nan() {
835 FontSizeAdjust::$basis(NonNegative(abs($fallback)))
836 } else {
837 FontSizeAdjust::$basis(NonNegative(ratio))
838 }
839 },
840 }
841 }};
842 }
843
844 match *self {
845 Self::None => FontSizeAdjust::None,
846 Self::ExHeight(val) => {
847 resolve!(
848 ExHeight,
849 val,
850 false,
851 x_height,
852 0.5,
853 QueryFontMetricsFlags::empty()
854 )
855 },
856 Self::CapHeight(val) => {
857 resolve!(
858 CapHeight,
859 val,
860 false,
861 cap_height,
862 -1.0, QueryFontMetricsFlags::empty()
864 )
865 },
866 Self::ChWidth(val) => {
867 resolve!(
868 ChWidth,
869 val,
870 false,
871 zero_advance_measure,
872 0.5,
873 QueryFontMetricsFlags::NEEDS_CH
874 )
875 },
876 Self::IcWidth(val) => {
877 resolve!(
878 IcWidth,
879 val,
880 false,
881 ic_width,
882 1.0,
883 QueryFontMetricsFlags::NEEDS_IC
884 )
885 },
886 Self::IcHeight(val) => {
887 resolve!(
888 IcHeight,
889 val,
890 true,
891 ic_width,
892 1.0,
893 QueryFontMetricsFlags::NEEDS_IC
894 )
895 },
896 }
897 }
898
899 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
900 macro_rules! case {
901 ($basis:ident, $val:expr) => {
902 Self::$basis(specified::FontSizeAdjustFactor::Number(
903 ToComputedValue::from_computed_value($val),
904 ))
905 };
906 }
907 match *computed {
908 FontSizeAdjust::None => Self::None,
909 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
910 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
911 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
912 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
913 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
914 }
915 }
916}
917
918pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
920
921pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
923
924fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
927where
928 T: TaggedFontValue,
929{
930 if settings_list.len() > 1 {
931 settings_list.sort_by_key(|k| k.tag().0);
932 let mut prev_tag = settings_list.last().unwrap().tag();
935 for i in (0..settings_list.len() - 1).rev() {
936 let cur_tag = settings_list[i].tag();
937 if cur_tag == prev_tag {
938 settings_list.remove(i);
939 }
940 prev_tag = cur_tag;
941 }
942 }
943}
944
945impl<T> ToComputedValue for FontSettings<T>
946where
947 T: ToComputedValue,
948 <T as ToComputedValue>::ComputedValue: TaggedFontValue,
949{
950 type ComputedValue = FontSettings<T::ComputedValue>;
951
952 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
953 let mut v = self
954 .0
955 .iter()
956 .map(|item| item.to_computed_value(context))
957 .collect::<Vec<_>>();
958 dedup_font_settings(&mut v);
959 FontSettings(v.into_boxed_slice())
960 }
961
962 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
963 Self(computed.0.iter().map(T::from_computed_value).collect())
964 }
965}
966
967#[derive(
972 Clone,
973 Copy,
974 Debug,
975 Eq,
976 MallocSizeOf,
977 PartialEq,
978 SpecifiedValueInfo,
979 ToComputedValue,
980 ToResolvedValue,
981 ToShmem,
982)]
983#[repr(C)]
984#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
985#[value_info(other_values = "normal")]
986pub struct FontLanguageOverride(pub u32);
987
988impl FontLanguageOverride {
989 #[inline]
990 pub fn normal() -> FontLanguageOverride {
992 FontLanguageOverride(0)
993 }
994
995 #[inline]
997 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
998 *storage = u32::to_be_bytes(self.0);
999 let slice = if cfg!(debug_assertions) {
1001 std::str::from_utf8(&storage[..]).unwrap()
1002 } else {
1003 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
1004 };
1005 slice.trim_end()
1006 }
1007
1008 #[inline]
1011 pub unsafe fn from_u32(value: u32) -> Self {
1012 Self(value)
1013 }
1014}
1015
1016impl ToCss for FontLanguageOverride {
1017 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1018 where
1019 W: fmt::Write,
1020 {
1021 if self.0 == 0 {
1022 return dest.write_str("normal");
1023 }
1024 self.to_str(&mut [0; 4]).to_css(dest)
1025 }
1026}
1027
1028impl ToComputedValue for specified::MozScriptMinSize {
1029 type ComputedValue = MozScriptMinSize;
1030
1031 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1032 let base_size = FontBaseSize::InheritedStyle;
1035 let line_height_base = LineHeightBase::InheritedStyle;
1036 match self.0 {
1037 NoCalcLength::FontRelative(value) => {
1038 value.to_computed_value(cx, base_size, line_height_base)
1039 },
1040 NoCalcLength::ServoCharacterWidth(value) => {
1041 value.to_computed_value(base_size.resolve(cx).computed_size())
1042 },
1043 ref l => l.to_computed_value(cx),
1044 }
1045 }
1046
1047 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1048 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1049 }
1050}
1051
1052pub type MathDepth = i8;
1054
1055#[cfg(feature = "gecko")]
1056impl ToComputedValue for specified::MathDepth {
1057 type ComputedValue = MathDepth;
1058
1059 fn to_computed_value(&self, cx: &Context) -> i8 {
1060 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1061 use std::{cmp, i8};
1062
1063 let int = match *self {
1064 specified::MathDepth::AutoAdd => {
1065 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
1066 let style = cx.builder.get_parent_font().clone_math_style();
1067 if style == MathStyleValue::Compact {
1068 parent.saturating_add(1)
1069 } else {
1070 parent
1071 }
1072 },
1073 specified::MathDepth::Add(rel) => {
1074 let parent = cx.builder.get_parent_font().clone_math_depth();
1075 (parent as i32).saturating_add(rel.to_computed_value(cx))
1076 },
1077 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1078 };
1079 cmp::min(int, i8::MAX as i32) as i8
1080 }
1081
1082 fn from_computed_value(other: &i8) -> Self {
1083 let computed_value = *other as i32;
1084 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1085 }
1086}
1087
1088pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1093
1094pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1097
1098#[derive(
1109 Clone,
1110 ComputeSquaredDistance,
1111 Copy,
1112 Debug,
1113 Eq,
1114 Hash,
1115 MallocSizeOf,
1116 PartialEq,
1117 PartialOrd,
1118 ToResolvedValue,
1119)]
1120#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1121#[repr(C)]
1122pub struct FontStyle(FontStyleFixedPoint);
1123
1124impl FontStyle {
1125 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1127 value: 0 << FONT_STYLE_FRACTION_BITS,
1128 });
1129
1130 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1132 value: 100 << FONT_STYLE_FRACTION_BITS,
1133 });
1134
1135 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1138
1139 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1141 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1142 });
1143
1144 #[inline]
1146 pub fn normal() -> Self {
1147 Self::NORMAL
1148 }
1149
1150 pub fn oblique(degrees: f32) -> Self {
1152 Self(FixedPoint::from_float(
1153 degrees
1154 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1155 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1156 ))
1157 }
1158
1159 pub fn oblique_degrees(&self) -> f32 {
1161 debug_assert_ne!(*self, Self::ITALIC);
1162 self.0.to_float()
1163 }
1164}
1165
1166impl ToCss for FontStyle {
1167 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1168 where
1169 W: fmt::Write,
1170 {
1171 if *self == Self::NORMAL {
1172 return dest.write_str("normal");
1173 }
1174 if *self == Self::ITALIC {
1175 return dest.write_str("italic");
1176 }
1177 dest.write_str("oblique")?;
1178 if *self != Self::OBLIQUE {
1179 dest.write_char(' ')?;
1181 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1182 }
1183 Ok(())
1184 }
1185}
1186
1187impl ToAnimatedValue for FontStyle {
1188 type AnimatedValue = generics::FontStyle<Angle>;
1189
1190 #[inline]
1191 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1192 if self == Self::ITALIC {
1193 return generics::FontStyle::Italic;
1194 }
1195 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1196 }
1197
1198 #[inline]
1199 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1200 match animated {
1201 generics::FontStyle::Italic => Self::ITALIC,
1202 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1203 }
1204 }
1205}
1206
1207pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
1214
1215pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
1218
1219#[derive(
1228 Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,
1229)]
1230#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1231#[repr(C)]
1232pub struct FontStretch(pub FontStretchFixedPoint);
1233
1234impl FontStretch {
1235 pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
1237 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
1239
1240 pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1242 value: 50 << Self::FRACTION_BITS,
1243 });
1244 pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1246 value: (62 << Self::FRACTION_BITS) + Self::HALF,
1247 });
1248 pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1250 value: 75 << Self::FRACTION_BITS,
1251 });
1252 pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1254 value: (87 << Self::FRACTION_BITS) + Self::HALF,
1255 });
1256 pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
1258 value: 100 << Self::FRACTION_BITS,
1259 });
1260 pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1262 value: (112 << Self::FRACTION_BITS) + Self::HALF,
1263 });
1264 pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1266 value: 125 << Self::FRACTION_BITS,
1267 });
1268 pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1270 value: 150 << Self::FRACTION_BITS,
1271 });
1272 pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1274 value: 200 << Self::FRACTION_BITS,
1275 });
1276
1277 pub fn hundred() -> Self {
1279 Self::NORMAL
1280 }
1281
1282 #[inline]
1284 pub fn to_percentage(&self) -> Percentage {
1285 Percentage(self.0.to_float() / 100.0)
1286 }
1287
1288 pub fn from_percentage(p: f32) -> Self {
1290 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
1291 }
1292
1293 pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
1296 use specified::FontStretchKeyword::*;
1297 match kw {
1298 UltraCondensed => Self::ULTRA_CONDENSED,
1299 ExtraCondensed => Self::EXTRA_CONDENSED,
1300 Condensed => Self::CONDENSED,
1301 SemiCondensed => Self::SEMI_CONDENSED,
1302 Normal => Self::NORMAL,
1303 SemiExpanded => Self::SEMI_EXPANDED,
1304 Expanded => Self::EXPANDED,
1305 ExtraExpanded => Self::EXTRA_EXPANDED,
1306 UltraExpanded => Self::ULTRA_EXPANDED,
1307 }
1308 }
1309
1310 pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
1312 use specified::FontStretchKeyword::*;
1313 if *self == Self::ULTRA_CONDENSED {
1315 return Some(UltraCondensed);
1316 }
1317 if *self == Self::EXTRA_CONDENSED {
1318 return Some(ExtraCondensed);
1319 }
1320 if *self == Self::CONDENSED {
1321 return Some(Condensed);
1322 }
1323 if *self == Self::SEMI_CONDENSED {
1324 return Some(SemiCondensed);
1325 }
1326 if *self == Self::NORMAL {
1327 return Some(Normal);
1328 }
1329 if *self == Self::SEMI_EXPANDED {
1330 return Some(SemiExpanded);
1331 }
1332 if *self == Self::EXPANDED {
1333 return Some(Expanded);
1334 }
1335 if *self == Self::EXTRA_EXPANDED {
1336 return Some(ExtraExpanded);
1337 }
1338 if *self == Self::ULTRA_EXPANDED {
1339 return Some(UltraExpanded);
1340 }
1341 None
1342 }
1343}
1344
1345impl ToCss for FontStretch {
1346 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1347 where
1348 W: fmt::Write,
1349 {
1350 self.to_percentage().to_css(dest)
1351 }
1352}
1353
1354impl ToAnimatedValue for FontStretch {
1355 type AnimatedValue = Percentage;
1356
1357 #[inline]
1358 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1359 self.to_percentage()
1360 }
1361
1362 #[inline]
1363 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1364 Self::from_percentage(animated.0)
1365 }
1366}
1367
1368pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1370
1371impl ToResolvedValue for LineHeight {
1372 type ResolvedValue = Self;
1373
1374 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1375 #[cfg(feature = "gecko")]
1376 {
1377 if matches!(self, Self::Normal | Self::MozBlockHeight) {
1379 return self;
1380 }
1381 let wm = context.style.writing_mode;
1382 Self::Length(
1383 context
1384 .device
1385 .calc_line_height(
1386 context.style.get_font(),
1387 wm,
1388 Some(context.element_info.element),
1389 )
1390 .to_resolved_value(context),
1391 )
1392 }
1393 #[cfg(feature = "servo")]
1394 {
1395 if let LineHeight::Number(num) = &self {
1396 let size = context.style.get_font().clone_font_size().computed_size();
1397 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1398 } else {
1399 self
1400 }
1401 }
1402 }
1403
1404 #[inline]
1405 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1406 value
1407 }
1408}