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)]
115#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
116#[css(comma)]
117pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);
118
119impl<T> FontSettings<T> {
120    /// Default value of font settings as `normal`.
121    #[inline]
122    pub fn normal() -> Self {
123        FontSettings(vec![].into_boxed_slice())
124    }
125}
126
127impl<T: Parse> Parse for FontSettings<T> {
128    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
129    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
130    fn parse<'i, 't>(
131        context: &ParserContext,
132        input: &mut Parser<'i, 't>,
133    ) -> Result<Self, ParseError<'i>> {
134        if input
135            .try_parse(|i| i.expect_ident_matching("normal"))
136            .is_ok()
137        {
138            return Ok(Self::normal());
139        }
140
141        Ok(FontSettings(
142            input
143                .parse_comma_separated(|i| T::parse(context, i))?
144                .into_boxed_slice(),
145        ))
146    }
147}
148
149/// A font four-character tag, represented as a u32 for convenience.
150///
151/// See:
152///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
153///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
154///
155#[derive(
156    Clone,
157    Copy,
158    Debug,
159    Eq,
160    MallocSizeOf,
161    PartialEq,
162    SpecifiedValueInfo,
163    ToAnimatedValue,
164    ToComputedValue,
165    ToResolvedValue,
166    ToShmem,
167)]
168#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
169pub struct FontTag(pub u32);
170
171impl ToCss for FontTag {
172    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
173    where
174        W: Write,
175    {
176        use byteorder::ByteOrder;
177        use std::str;
178
179        let mut raw = [0u8; 4];
180        BigEndian::write_u32(&mut raw, self.0);
181        str::from_utf8(&raw).unwrap_or_default().to_css(dest)
182    }
183}
184
185impl Parse for FontTag {
186    fn parse<'i, 't>(
187        _context: &ParserContext,
188        input: &mut Parser<'i, 't>,
189    ) -> Result<Self, ParseError<'i>> {
190        let location = input.current_source_location();
191        let tag = input.expect_string()?;
192
193        // allowed strings of length 4 containing chars: <U+20, U+7E>
194        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
195            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
196        }
197
198        let mut raw = Cursor::new(tag.as_bytes());
199        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
200    }
201}
202
203/// A generic value for the `font-style` property.
204///
205/// https://drafts.csswg.org/css-fonts-4/#font-style-prop
206#[allow(missing_docs)]
207#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
208#[derive(
209    Animate,
210    Clone,
211    ComputeSquaredDistance,
212    Copy,
213    Debug,
214    Hash,
215    MallocSizeOf,
216    PartialEq,
217    SpecifiedValueInfo,
218    ToAnimatedValue,
219    ToAnimatedZero,
220    ToResolvedValue,
221    ToShmem,
222)]
223#[value_info(other_values = "normal")]
224pub enum FontStyle<Angle> {
225    // Note that 'oblique 0deg' represents 'normal', and will serialize as such.
226    #[value_info(starts_with_keyword)]
227    Oblique(Angle),
228    #[animation(error)]
229    Italic,
230}
231
232impl<Angle: Zero> FontStyle<Angle> {
233    /// Return the 'normal' value, which is represented as 'oblique 0deg'.
234    pub fn normal() -> Self {
235        Self::Oblique(Angle::zero())
236    }
237}
238
239/// A generic value for the `font-size-adjust` property.
240///
241/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop
242#[allow(missing_docs)]
243#[repr(u8)]
244#[derive(
245    Animate,
246    Clone,
247    ComputeSquaredDistance,
248    Copy,
249    Debug,
250    Hash,
251    MallocSizeOf,
252    PartialEq,
253    SpecifiedValueInfo,
254    ToAnimatedValue,
255    ToAnimatedZero,
256    ToComputedValue,
257    ToResolvedValue,
258    ToShmem,
259)]
260pub enum GenericFontSizeAdjust<Factor> {
261    #[animation(error)]
262    None,
263    #[value_info(starts_with_keyword)]
264    ExHeight(Factor),
265    #[value_info(starts_with_keyword)]
266    CapHeight(Factor),
267    #[value_info(starts_with_keyword)]
268    ChWidth(Factor),
269    #[value_info(starts_with_keyword)]
270    IcWidth(Factor),
271    #[value_info(starts_with_keyword)]
272    IcHeight(Factor),
273}
274
275impl<Factor: ToCss> ToCss for GenericFontSizeAdjust<Factor> {
276    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
277    where
278        W: Write,
279    {
280        let (prefix, value) = match self {
281            Self::None => return dest.write_str("none"),
282            Self::ExHeight(v) => ("", v),
283            Self::CapHeight(v) => ("cap-height ", v),
284            Self::ChWidth(v) => ("ch-width ", v),
285            Self::IcWidth(v) => ("ic-width ", v),
286            Self::IcHeight(v) => ("ic-height ", v),
287        };
288
289        dest.write_str(prefix)?;
290        value.to_css(dest)
291    }
292}
293
294/// A generic value for the `line-height` property.
295#[derive(
296    Animate,
297    Clone,
298    ComputeSquaredDistance,
299    Copy,
300    Debug,
301    MallocSizeOf,
302    PartialEq,
303    SpecifiedValueInfo,
304    ToAnimatedValue,
305    ToCss,
306    ToShmem,
307    Parse,
308)]
309#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
310#[repr(C, u8)]
311pub enum GenericLineHeight<N, L> {
312    /// `normal`
313    Normal,
314    /// `-moz-block-height`
315    #[cfg(feature = "gecko")]
316    #[parse(condition = "ParserContext::in_ua_sheet")]
317    MozBlockHeight,
318    /// `<number>`
319    Number(N),
320    /// `<length-percentage>`
321    Length(L),
322}
323
324pub use self::GenericLineHeight as LineHeight;
325
326impl<N, L> ToAnimatedZero for LineHeight<N, L> {
327    #[inline]
328    fn to_animated_zero(&self) -> Result<Self, ()> {
329        Err(())
330    }
331}
332
333impl<N, L> LineHeight<N, L> {
334    /// Returns `normal`.
335    #[inline]
336    pub fn normal() -> Self {
337        LineHeight::Normal
338    }
339}