style/values/generics/
font.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//! Generic types for font stuff.
6
7use crate::parser::{Parse, ParserContext};
8use crate::values::animated::ToAnimatedZero;
9use crate::{One, Zero};
10use byteorder::{BigEndian, ReadBytesExt};
11use cssparser::Parser;
12use std::fmt::{self, Write};
13use std::io::Cursor;
14use style_traits::{CssWriter, ParseError};
15use style_traits::{StyleParseErrorKind, ToCss};
16
17/// A trait for values that are labelled with a FontTag (for feature and
18/// variation settings).
19pub trait TaggedFontValue {
20    /// The value's tag.
21    fn tag(&self) -> FontTag;
22}
23
24/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
25#[derive(
26    Clone,
27    Debug,
28    Eq,
29    MallocSizeOf,
30    PartialEq,
31    SpecifiedValueInfo,
32    ToAnimatedValue,
33    ToComputedValue,
34    ToResolvedValue,
35    ToShmem,
36)]
37pub struct FeatureTagValue<Integer> {
38    /// A four-character tag, packed into a u32 (one byte per character).
39    pub tag: FontTag,
40    /// The actual value.
41    pub value: Integer,
42}
43
44impl<T> TaggedFontValue for FeatureTagValue<T> {
45    fn tag(&self) -> FontTag {
46        self.tag
47    }
48}
49
50impl<Integer> ToCss for FeatureTagValue<Integer>
51where
52    Integer: One + ToCss + PartialEq,
53{
54    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
55    where
56        W: Write,
57    {
58        self.tag.to_css(dest)?;
59        // Don't serialize the default value.
60        if !self.value.is_one() {
61            dest.write_char(' ')?;
62            self.value.to_css(dest)?;
63        }
64
65        Ok(())
66    }
67}
68
69/// Variation setting for a single feature, see:
70///
71/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
72#[derive(
73    Animate,
74    Clone,
75    ComputeSquaredDistance,
76    Debug,
77    Eq,
78    MallocSizeOf,
79    PartialEq,
80    SpecifiedValueInfo,
81    ToAnimatedValue,
82    ToComputedValue,
83    ToCss,
84    ToResolvedValue,
85    ToShmem,
86)]
87#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
88pub struct VariationValue<Number> {
89    /// A four-character tag, packed into a u32 (one byte per character).
90    #[animation(constant)]
91    pub tag: FontTag,
92    /// The actual value.
93    pub value: Number,
94}
95
96impl<T> TaggedFontValue for VariationValue<T> {
97    fn tag(&self) -> FontTag {
98        self.tag
99    }
100}
101
102/// A value both for font-variation-settings and font-feature-settings.
103#[derive(
104    Clone,
105    Debug,
106    Eq,
107    MallocSizeOf,
108    PartialEq,
109    SpecifiedValueInfo,
110    ToAnimatedValue,
111    ToCss,
112    ToResolvedValue,
113    ToShmem,
114    ToTyped,
115)]
116#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
117#[css(comma)]
118pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
119
120impl<T> FontSettings<T> {
121    /// Default value of font settings as `normal`.
122    #[inline]
123    pub fn normal() -> Self {
124        FontSettings(vec![].into_boxed_slice())
125    }
126}
127
128impl<T: Parse> Parse for FontSettings<T> {
129    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
130    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
131    fn parse<'i, 't>(
132        context: &ParserContext,
133        input: &mut Parser<'i, 't>,
134    ) -> Result<Self, ParseError<'i>> {
135        if input
136            .try_parse(|i| i.expect_ident_matching("normal"))
137            .is_ok()
138        {
139            return Ok(Self::normal());
140        }
141
142        Ok(FontSettings(
143            input
144                .parse_comma_separated(|i| T::parse(context, i))?
145                .into_boxed_slice(),
146        ))
147    }
148}
149
150/// A font four-character tag, represented as a u32 for convenience.
151///
152/// See:
153///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
154///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
155///
156#[derive(
157    Clone,
158    Copy,
159    Debug,
160    Eq,
161    MallocSizeOf,
162    PartialEq,
163    SpecifiedValueInfo,
164    ToAnimatedValue,
165    ToComputedValue,
166    ToResolvedValue,
167    ToShmem,
168)]
169#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
170pub struct FontTag(pub u32);
171
172impl ToCss for FontTag {
173    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
174    where
175        W: Write,
176    {
177        use byteorder::ByteOrder;
178        use std::str;
179
180        let mut raw = [0u8; 4];
181        BigEndian::write_u32(&mut raw, self.0);
182        str::from_utf8(&raw).unwrap_or_default().to_css(dest)
183    }
184}
185
186impl Parse for FontTag {
187    fn parse<'i, 't>(
188        _context: &ParserContext,
189        input: &mut Parser<'i, 't>,
190    ) -> Result<Self, ParseError<'i>> {
191        let location = input.current_source_location();
192        let tag = input.expect_string()?;
193
194        // allowed strings of length 4 containing chars: <U+20, U+7E>
195        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
196            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
197        }
198
199        let mut raw = Cursor::new(tag.as_bytes());
200        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
201    }
202}
203
204/// A generic value for the `font-style` property.
205///
206/// https://drafts.csswg.org/css-fonts-4/#font-style-prop
207#[allow(missing_docs)]
208#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
209#[derive(
210    Animate,
211    Clone,
212    ComputeSquaredDistance,
213    Copy,
214    Debug,
215    Hash,
216    MallocSizeOf,
217    PartialEq,
218    SpecifiedValueInfo,
219    ToAnimatedValue,
220    ToAnimatedZero,
221    ToResolvedValue,
222    ToShmem,
223)]
224#[value_info(other_values = "normal")]
225pub enum FontStyle<Angle> {
226    // Note that 'oblique 0deg' represents 'normal', and will serialize as such.
227    #[value_info(starts_with_keyword)]
228    Oblique(Angle),
229    #[animation(error)]
230    Italic,
231}
232
233impl<Angle: Zero> FontStyle<Angle> {
234    /// Return the 'normal' value, which is represented as 'oblique 0deg'.
235    pub fn normal() -> Self {
236        Self::Oblique(Angle::zero())
237    }
238}
239
240/// A generic value for the `font-size-adjust` property.
241///
242/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop
243#[allow(missing_docs)]
244#[repr(u8)]
245#[derive(
246    Animate,
247    Clone,
248    ComputeSquaredDistance,
249    Copy,
250    Debug,
251    Hash,
252    MallocSizeOf,
253    PartialEq,
254    SpecifiedValueInfo,
255    ToAnimatedValue,
256    ToAnimatedZero,
257    ToComputedValue,
258    ToResolvedValue,
259    ToShmem,
260    ToTyped,
261)]
262pub enum GenericFontSizeAdjust<Factor> {
263    #[animation(error)]
264    None,
265    #[value_info(starts_with_keyword)]
266    ExHeight(Factor),
267    #[value_info(starts_with_keyword)]
268    CapHeight(Factor),
269    #[value_info(starts_with_keyword)]
270    ChWidth(Factor),
271    #[value_info(starts_with_keyword)]
272    IcWidth(Factor),
273    #[value_info(starts_with_keyword)]
274    IcHeight(Factor),
275}
276
277impl<Factor: ToCss> ToCss for GenericFontSizeAdjust<Factor> {
278    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
279    where
280        W: Write,
281    {
282        let (prefix, value) = match self {
283            Self::None => return dest.write_str("none"),
284            Self::ExHeight(v) => ("", v),
285            Self::CapHeight(v) => ("cap-height ", v),
286            Self::ChWidth(v) => ("ch-width ", v),
287            Self::IcWidth(v) => ("ic-width ", v),
288            Self::IcHeight(v) => ("ic-height ", v),
289        };
290
291        dest.write_str(prefix)?;
292        value.to_css(dest)
293    }
294}
295
296/// A generic value for the `line-height` property.
297#[derive(
298    Animate,
299    Clone,
300    ComputeSquaredDistance,
301    Copy,
302    Debug,
303    MallocSizeOf,
304    PartialEq,
305    SpecifiedValueInfo,
306    ToAnimatedValue,
307    ToCss,
308    ToShmem,
309    Parse,
310    ToTyped,
311)]
312#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
313#[repr(C, u8)]
314pub enum GenericLineHeight<N, L> {
315    /// `normal`
316    Normal,
317    /// `-moz-block-height`
318    #[cfg(feature = "gecko")]
319    #[parse(condition = "ParserContext::in_ua_sheet")]
320    MozBlockHeight,
321    /// `<number>`
322    Number(N),
323    /// `<length-percentage>`
324    Length(L),
325}
326
327pub use self::GenericLineHeight as LineHeight;
328
329impl<N, L> ToAnimatedZero for LineHeight<N, L> {
330    #[inline]
331    fn to_animated_zero(&self) -> Result<Self, ()> {
332        Err(())
333    }
334}
335
336impl<N, L> LineHeight<N, L> {
337    /// Returns `normal`.
338    #[inline]
339    pub fn normal() -> Self {
340        LineHeight::Normal
341    }
342}