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!(ExHeight, val, false, x_height, 0.5, QueryFontMetricsFlags::empty())
848 },
849 Self::CapHeight(val) => {
850 resolve!(CapHeight, val, false, cap_height, -1.0 , QueryFontMetricsFlags::empty())
851 },
852 Self::ChWidth(val) => {
853 resolve!(ChWidth, val, false, zero_advance_measure, 0.5, QueryFontMetricsFlags::NEEDS_CH)
854 },
855 Self::IcWidth(val) => {
856 resolve!(IcWidth, val, false, ic_width, 1.0, QueryFontMetricsFlags::NEEDS_IC)
857 },
858 Self::IcHeight(val) => {
859 resolve!(IcHeight, val, true, ic_width, 1.0, QueryFontMetricsFlags::NEEDS_IC)
860 },
861 }
862 }
863
864 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
865 macro_rules! case {
866 ($basis:ident, $val:expr) => {
867 Self::$basis(specified::FontSizeAdjustFactor::Number(
868 ToComputedValue::from_computed_value($val),
869 ))
870 };
871 }
872 match *computed {
873 FontSizeAdjust::None => Self::None,
874 FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),
875 FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),
876 FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),
877 FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),
878 FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),
879 }
880 }
881}
882
883pub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;
885
886pub type FontVariationSettings = FontSettings<VariationValue<Number>>;
888
889fn dedup_font_settings<T>(settings_list: &mut Vec<T>)
892where
893 T: TaggedFontValue,
894{
895 if settings_list.len() > 1 {
896 settings_list.sort_by_key(|k| k.tag().0);
897 let mut prev_tag = settings_list.last().unwrap().tag();
900 for i in (0..settings_list.len() - 1).rev() {
901 let cur_tag = settings_list[i].tag();
902 if cur_tag == prev_tag {
903 settings_list.remove(i);
904 }
905 prev_tag = cur_tag;
906 }
907 }
908}
909
910impl<T> ToComputedValue for FontSettings<T>
911where
912 T: ToComputedValue,
913 <T as ToComputedValue>::ComputedValue: TaggedFontValue,
914{
915 type ComputedValue = FontSettings<T::ComputedValue>;
916
917 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
918 let mut v = self
919 .0
920 .iter()
921 .map(|item| item.to_computed_value(context))
922 .collect::<Vec<_>>();
923 dedup_font_settings(&mut v);
924 FontSettings(v.into_boxed_slice())
925 }
926
927 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
928 Self(
929 computed
930 .0
931 .iter()
932 .map(T::from_computed_value)
933 .collect()
934 )
935 }
936}
937
938#[derive(
943 Clone,
944 Copy,
945 Debug,
946 Eq,
947 MallocSizeOf,
948 PartialEq,
949 SpecifiedValueInfo,
950 ToComputedValue,
951 ToResolvedValue,
952 ToShmem,
953)]
954#[repr(C)]
955#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
956#[value_info(other_values = "normal")]
957pub struct FontLanguageOverride(pub u32);
958
959impl FontLanguageOverride {
960 #[inline]
961 pub fn normal() -> FontLanguageOverride {
963 FontLanguageOverride(0)
964 }
965
966 #[inline]
968 pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {
969 *storage = u32::to_be_bytes(self.0);
970 let slice = if cfg!(debug_assertions) {
972 std::str::from_utf8(&storage[..]).unwrap()
973 } else {
974 unsafe { std::str::from_utf8_unchecked(&storage[..]) }
975 };
976 slice.trim_end()
977 }
978
979 #[inline]
982 pub unsafe fn from_u32(value: u32) -> Self {
983 Self(value)
984 }
985}
986
987impl ToCss for FontLanguageOverride {
988 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
989 where
990 W: fmt::Write,
991 {
992 if self.0 == 0 {
993 return dest.write_str("normal");
994 }
995 self.to_str(&mut [0; 4]).to_css(dest)
996 }
997}
998
999impl ToComputedValue for specified::MozScriptMinSize {
1000 type ComputedValue = MozScriptMinSize;
1001
1002 fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {
1003 let base_size = FontBaseSize::InheritedStyle;
1006 let line_height_base = LineHeightBase::InheritedStyle;
1007 match self.0 {
1008 NoCalcLength::FontRelative(value) => {
1009 value.to_computed_value(cx, base_size, line_height_base)
1010 },
1011 NoCalcLength::ServoCharacterWidth(value) => {
1012 value.to_computed_value(base_size.resolve(cx).computed_size())
1013 },
1014 ref l => l.to_computed_value(cx),
1015 }
1016 }
1017
1018 fn from_computed_value(other: &MozScriptMinSize) -> Self {
1019 specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))
1020 }
1021}
1022
1023pub type MathDepth = i8;
1025
1026#[cfg(feature = "gecko")]
1027impl ToComputedValue for specified::MathDepth {
1028 type ComputedValue = MathDepth;
1029
1030 fn to_computed_value(&self, cx: &Context) -> i8 {
1031 use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;
1032 use std::{cmp, i8};
1033
1034 let int = match *self {
1035 specified::MathDepth::AutoAdd => {
1036 let parent = cx.builder.get_parent_font().clone_math_depth() as i32;
1037 let style = cx.builder.get_parent_font().clone_math_style();
1038 if style == MathStyleValue::Compact {
1039 parent.saturating_add(1)
1040 } else {
1041 parent
1042 }
1043 },
1044 specified::MathDepth::Add(rel) => {
1045 let parent = cx.builder.get_parent_font().clone_math_depth();
1046 (parent as i32).saturating_add(rel.to_computed_value(cx))
1047 },
1048 specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),
1049 };
1050 cmp::min(int, i8::MAX as i32) as i8
1051 }
1052
1053 fn from_computed_value(other: &i8) -> Self {
1054 let computed_value = *other as i32;
1055 specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))
1056 }
1057}
1058
1059pub const FONT_STYLE_FRACTION_BITS: u16 = 8;
1064
1065pub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;
1068
1069#[derive(
1080 Clone,
1081 ComputeSquaredDistance,
1082 Copy,
1083 Debug,
1084 Eq,
1085 Hash,
1086 MallocSizeOf,
1087 PartialEq,
1088 PartialOrd,
1089 ToResolvedValue,
1090)]
1091#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1092#[repr(C)]
1093pub struct FontStyle(FontStyleFixedPoint);
1094
1095impl FontStyle {
1096 pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {
1098 value: 0 << FONT_STYLE_FRACTION_BITS,
1099 });
1100
1101 pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {
1103 value: 100 << FONT_STYLE_FRACTION_BITS,
1104 });
1105
1106 pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;
1109
1110 pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {
1112 value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,
1113 });
1114
1115 #[inline]
1117 pub fn normal() -> Self {
1118 Self::NORMAL
1119 }
1120
1121 pub fn oblique(degrees: f32) -> Self {
1123 Self(FixedPoint::from_float(
1124 degrees
1125 .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)
1126 .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),
1127 ))
1128 }
1129
1130 pub fn oblique_degrees(&self) -> f32 {
1132 debug_assert_ne!(*self, Self::ITALIC);
1133 self.0.to_float()
1134 }
1135}
1136
1137impl ToCss for FontStyle {
1138 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1139 where
1140 W: fmt::Write,
1141 {
1142 if *self == Self::NORMAL {
1143 return dest.write_str("normal");
1144 }
1145 if *self == Self::ITALIC {
1146 return dest.write_str("italic");
1147 }
1148 dest.write_str("oblique")?;
1149 if *self != Self::OBLIQUE {
1150 dest.write_char(' ')?;
1152 Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;
1153 }
1154 Ok(())
1155 }
1156}
1157
1158impl ToAnimatedValue for FontStyle {
1159 type AnimatedValue = generics::FontStyle<Angle>;
1160
1161 #[inline]
1162 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1163 if self == Self::ITALIC {
1164 return generics::FontStyle::Italic;
1165 }
1166 generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))
1167 }
1168
1169 #[inline]
1170 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1171 match animated {
1172 generics::FontStyle::Italic => Self::ITALIC,
1173 generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),
1174 }
1175 }
1176}
1177
1178pub const FONT_STRETCH_FRACTION_BITS: u16 = 6;
1185
1186pub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;
1189
1190#[derive(
1199 Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,
1200)]
1201#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
1202#[repr(C)]
1203pub struct FontStretch(pub FontStretchFixedPoint);
1204
1205impl FontStretch {
1206 pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;
1208 pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);
1210
1211 pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1213 value: 50 << Self::FRACTION_BITS,
1214 });
1215 pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1217 value: (62 << Self::FRACTION_BITS) + Self::HALF,
1218 });
1219 pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1221 value: 75 << Self::FRACTION_BITS,
1222 });
1223 pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {
1225 value: (87 << Self::FRACTION_BITS) + Self::HALF,
1226 });
1227 pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {
1229 value: 100 << Self::FRACTION_BITS,
1230 });
1231 pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1233 value: (112 << Self::FRACTION_BITS) + Self::HALF,
1234 });
1235 pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1237 value: 125 << Self::FRACTION_BITS,
1238 });
1239 pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1241 value: 150 << Self::FRACTION_BITS,
1242 });
1243 pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {
1245 value: 200 << Self::FRACTION_BITS,
1246 });
1247
1248 pub fn hundred() -> Self {
1250 Self::NORMAL
1251 }
1252
1253 #[inline]
1255 pub fn to_percentage(&self) -> Percentage {
1256 Percentage(self.0.to_float() / 100.0)
1257 }
1258
1259 pub fn from_percentage(p: f32) -> Self {
1261 Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))
1262 }
1263
1264 pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {
1267 use specified::FontStretchKeyword::*;
1268 match kw {
1269 UltraCondensed => Self::ULTRA_CONDENSED,
1270 ExtraCondensed => Self::EXTRA_CONDENSED,
1271 Condensed => Self::CONDENSED,
1272 SemiCondensed => Self::SEMI_CONDENSED,
1273 Normal => Self::NORMAL,
1274 SemiExpanded => Self::SEMI_EXPANDED,
1275 Expanded => Self::EXPANDED,
1276 ExtraExpanded => Self::EXTRA_EXPANDED,
1277 UltraExpanded => Self::ULTRA_EXPANDED,
1278 }
1279 }
1280
1281 pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {
1283 use specified::FontStretchKeyword::*;
1284 if *self == Self::ULTRA_CONDENSED {
1286 return Some(UltraCondensed);
1287 }
1288 if *self == Self::EXTRA_CONDENSED {
1289 return Some(ExtraCondensed);
1290 }
1291 if *self == Self::CONDENSED {
1292 return Some(Condensed);
1293 }
1294 if *self == Self::SEMI_CONDENSED {
1295 return Some(SemiCondensed);
1296 }
1297 if *self == Self::NORMAL {
1298 return Some(Normal);
1299 }
1300 if *self == Self::SEMI_EXPANDED {
1301 return Some(SemiExpanded);
1302 }
1303 if *self == Self::EXPANDED {
1304 return Some(Expanded);
1305 }
1306 if *self == Self::EXTRA_EXPANDED {
1307 return Some(ExtraExpanded);
1308 }
1309 if *self == Self::ULTRA_EXPANDED {
1310 return Some(UltraExpanded);
1311 }
1312 None
1313 }
1314}
1315
1316impl ToCss for FontStretch {
1317 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1318 where
1319 W: fmt::Write,
1320 {
1321 self.to_percentage().to_css(dest)
1322 }
1323}
1324
1325impl ToAnimatedValue for FontStretch {
1326 type AnimatedValue = Percentage;
1327
1328 #[inline]
1329 fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {
1330 self.to_percentage()
1331 }
1332
1333 #[inline]
1334 fn from_animated_value(animated: Self::AnimatedValue) -> Self {
1335 Self::from_percentage(animated.0)
1336 }
1337}
1338
1339pub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
1341
1342impl ToResolvedValue for LineHeight {
1343 type ResolvedValue = Self;
1344
1345 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
1346 #[cfg(feature = "gecko")]
1347 {
1348 if matches!(self, Self::Normal | Self::MozBlockHeight) {
1350 return self;
1351 }
1352 let wm = context.style.writing_mode;
1353 Self::Length(
1354 context
1355 .device
1356 .calc_line_height(
1357 context.style.get_font(),
1358 wm,
1359 Some(context.element_info.element),
1360 )
1361 .to_resolved_value(context),
1362 )
1363 }
1364 #[cfg(feature = "servo")]
1365 {
1366 if let LineHeight::Number(num) = &self {
1367 let size = context.style.get_font().clone_font_size().computed_size();
1368 LineHeight::Length(NonNegativeLength::new(size.px() * num.0))
1369 } else {
1370 self
1371 }
1372 }
1373 }
1374
1375 #[inline]
1376 fn from_resolved_value(value: Self::ResolvedValue) -> Self {
1377 value
1378 }
1379}