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