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