Skip to main content

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