Skip to main content

style/values/computed/
text.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Computed types for text properties.
6
7use crate::derives::*;
8#[cfg(feature = "gecko")]
9use crate::gecko_bindings::bindings;
10use crate::typed_om::{KeywordValue, ToTyped, TypedValue};
11use crate::values::animated::text::TextDecorationInset as AnimatedTextDecorationInset;
12use crate::values::animated::{Context as AnimatedContext, ToAnimatedValue};
13use crate::values::computed::length::{CSSPixelLength, LengthPercentage};
14use crate::values::generics::text::{
15    GenericHyphenateLimitChars, GenericInitialLetter, GenericTextDecorationInset,
16    GenericTextDecorationLength, GenericTextIndent,
17};
18use crate::values::generics::NumberOrAuto;
19use crate::values::specified::text as specified;
20use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
21use crate::values::{CSSFloat, CSSInteger, ComputeSquaredDistance};
22use crate::Zero;
23use std::fmt::{self, Write};
24use style_traits::{CssString, CssWriter, ToCss};
25use thin_vec::ThinVec;
26
27pub use crate::values::specified::text::{
28    HyphenateCharacter, LineBreak, MozControlCharacterVisibility, OverflowWrap, RubyPosition,
29    TextAlignLast, TextAutospace, TextBoxEdge, TextBoxTrim, TextDecorationLine,
30    TextDecorationSkipInk, TextEmphasisPosition, TextJustify, TextOverflow, TextTransform,
31    TextUnderlinePosition, WordBreak,
32};
33
34/// A computed value for the `initial-letter` property.
35pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
36
37/// Implements type for `text-decoration-thickness` property.
38pub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;
39
40/// Implements type for `text-decoration-inset` property.
41pub type TextDecorationInset = GenericTextDecorationInset<LengthPercentage>;
42
43impl ToAnimatedValue for TextDecorationInset {
44    type AnimatedValue = AnimatedTextDecorationInset;
45
46    fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {
47        match self {
48            Self::Auto => {
49                let font_size_px = context
50                    .style
51                    .get_font()
52                    .clone_font_size()
53                    .computed_size()
54                    .px();
55                #[cfg(feature = "gecko")]
56                let auto_length = bindings::Gecko_CalcAutoDecorationInset(font_size_px);
57                #[cfg(feature = "servo")]
58                let auto_length = {
59                    // Use an inset factor of 1/12.5, so we get 2px of inset (resulting in 4px
60                    // gap between adjacent lines) at font-size 25px.
61                    let auto_inset_factor = 1.0 / 12.5;
62
63                    // Use the em size multiplied by auto_inset_factor, with a minimum of one
64                    // CSS pixel to ensure that at least some separation occurs.
65                    (font_size_px * auto_inset_factor).max(1.0)
66                };
67                let auto_length = CSSPixelLength::new(auto_length);
68                Self::AnimatedValue {
69                    start: LengthPercentage::new_length(auto_length),
70                    end: LengthPercentage::new_length(auto_length),
71                    is_auto: true,
72                }
73            },
74            Self::LengthPercentage { start, end } => Self::AnimatedValue {
75                start: start.to_animated_value(context),
76                end: end.to_animated_value(context),
77                is_auto: false,
78            },
79        }
80    }
81
82    #[inline]
83    fn from_animated_value(value: Self::AnimatedValue) -> Self {
84        if value.is_auto {
85            Self::Auto
86        } else {
87            Self::LengthPercentage {
88                start: value.start,
89                end: value.end,
90            }
91        }
92    }
93}
94
95/// The computed value of `text-align`.
96pub type TextAlign = specified::TextAlignKeyword;
97
98/// The computed value of `text-indent`.
99pub type TextIndent = GenericTextIndent<LengthPercentage>;
100
101/// A computed value for the `hyphenate-character` property.
102pub type HyphenateLimitChars = GenericHyphenateLimitChars<CSSInteger>;
103
104impl HyphenateLimitChars {
105    /// Return the `auto` value, which has all three component values as `auto`.
106    #[inline]
107    pub fn auto() -> Self {
108        Self {
109            total_word_length: NumberOrAuto::Auto,
110            pre_hyphen_length: NumberOrAuto::Auto,
111            post_hyphen_length: NumberOrAuto::Auto,
112        }
113    }
114}
115
116/// A computed value for the `letter-spacing` property.
117#[repr(transparent)]
118#[derive(
119    Animate,
120    Clone,
121    ComputeSquaredDistance,
122    Copy,
123    Debug,
124    MallocSizeOf,
125    PartialEq,
126    ToAnimatedValue,
127    ToAnimatedZero,
128    ToResolvedValue,
129)]
130pub struct GenericLetterSpacing<L>(pub L);
131/// This is generic just to make the #[derive()] code do the right thing for lengths.
132pub type LetterSpacing = GenericLetterSpacing<LengthPercentage>;
133
134impl LetterSpacing {
135    /// Return the `normal` computed value, which is just zero.
136    #[inline]
137    pub fn normal() -> Self {
138        Self(LengthPercentage::zero())
139    }
140}
141
142impl ToCss for LetterSpacing {
143    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
144    where
145        W: Write,
146    {
147        // https://drafts.csswg.org/css-text/#propdef-letter-spacing
148        //
149        // For legacy reasons, a computed letter-spacing of zero yields a
150        // resolved value (getComputedStyle() return value) of normal.
151        if self.0.is_zero() {
152            return dest.write_str("normal");
153        }
154        self.0.to_css(dest)
155    }
156}
157
158impl ToTyped for LetterSpacing {
159    // Note: The specification does not currently define how letter spacing
160    // should be reified into Typed OM. The current behavior follows existing
161    // WPT coverage (letter-spacing.html). Syncing spec with UA/WPT behavior
162    // tracked in https://github.com/w3c/csswg-drafts/issues/13907
163    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
164        if !self.0.has_percentage() && self.0.is_zero() {
165            dest.push(TypedValue::Keyword(KeywordValue(CssString::from("normal"))));
166            return Ok(());
167        }
168        self.0.to_typed(dest)
169    }
170}
171
172/// A computed value for the `word-spacing` property.
173pub type WordSpacing = LengthPercentage;
174
175impl WordSpacing {
176    /// Return the `normal` computed value, which is just zero.
177    #[inline]
178    pub fn normal() -> Self {
179        LengthPercentage::zero()
180    }
181}
182
183/// Computed value for the text-emphasis-style property
184#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToTyped)]
185#[allow(missing_docs)]
186#[repr(C, u8)]
187#[typed(todo_derive_fields)]
188pub enum TextEmphasisStyle {
189    /// [ <fill> || <shape> ]
190    Keyword {
191        #[css(skip_if = "TextEmphasisFillMode::is_filled")]
192        fill: TextEmphasisFillMode,
193        shape: TextEmphasisShapeKeyword,
194    },
195    /// `none`
196    None,
197    /// `<string>` (of which only the first grapheme cluster will be used).
198    String(crate::OwnedStr),
199}