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