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::typed_om::{KeywordValue, ToTyped, TypedValue};
10use crate::values::animated::ToAnimatedZero;
11use crate::{One, Zero};
12use byteorder::{BigEndian, ReadBytesExt};
13use cssparser::Parser;
14use std::fmt::{self, Write};
15use std::io::Cursor;
16use style_traits::{CssString, CssWriter, ParseError, StyleParseErrorKind, ToCss};
17use thin_vec::ThinVec;
18
19/// A trait for values that are labelled with a FontTag (for feature and
20/// variation settings).
21pub trait TaggedFontValue {
22    /// The value's tag.
23    fn tag(&self) -> FontTag;
24}
25
26/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
27#[derive(
28    Clone,
29    Debug,
30    Eq,
31    MallocSizeOf,
32    PartialEq,
33    SpecifiedValueInfo,
34    ToAnimatedValue,
35    ToComputedValue,
36    ToResolvedValue,
37    ToShmem,
38)]
39#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
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, Hash, 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    Eq,
164    MallocSizeOf,
165    PartialEq,
166    SpecifiedValueInfo,
167    ToAnimatedValue,
168    ToComputedValue,
169    ToResolvedValue,
170    ToShmem,
171)]
172#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))]
173pub struct FontTag(pub u32);
174
175impl fmt::Debug for FontTag {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        let tag_bytes = self.0.to_be_bytes();
178
179        let mut tuple = f.debug_tuple("FontTag");
180        if let Ok(utf8_tag) = str::from_utf8(&tag_bytes) {
181            tuple.field(&utf8_tag);
182        } else {
183            tuple.field(&tag_bytes);
184        };
185        tuple.finish()
186    }
187}
188
189impl ToCss for FontTag {
190    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
191    where
192        W: Write,
193    {
194        use byteorder::ByteOrder;
195        use std::str;
196
197        let mut raw = [0u8; 4];
198        BigEndian::write_u32(&mut raw, self.0);
199        str::from_utf8(&raw).unwrap_or_default().to_css(dest)
200    }
201}
202
203impl Parse for FontTag {
204    fn parse<'i, 't>(
205        _context: &ParserContext,
206        input: &mut Parser<'i, 't>,
207    ) -> Result<Self, ParseError<'i>> {
208        let location = input.current_source_location();
209        let tag = input.expect_string()?;
210
211        // allowed strings of length 4 containing chars: <U+20, U+7E>
212        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
213            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
214        }
215
216        let mut raw = Cursor::new(tag.as_bytes());
217        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
218    }
219}
220
221/// A generic value for the `font-style` property.
222///
223/// https://drafts.csswg.org/css-fonts-4/#font-style-prop
224#[allow(missing_docs)]
225#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
226#[derive(
227    Animate,
228    Clone,
229    ComputeSquaredDistance,
230    Copy,
231    Debug,
232    Hash,
233    MallocSizeOf,
234    PartialEq,
235    SpecifiedValueInfo,
236    ToAnimatedValue,
237    ToAnimatedZero,
238    ToResolvedValue,
239    ToShmem,
240)]
241#[value_info(other_values = "normal")]
242pub enum FontStyle<Angle> {
243    // Note that 'oblique 0deg' represents 'normal', and will serialize as such.
244    #[value_info(starts_with_keyword)]
245    Oblique(Angle),
246    #[animation(error)]
247    Italic,
248}
249
250impl<Angle: Zero> FontStyle<Angle> {
251    /// Return the 'normal' value, which is represented as 'oblique 0deg'.
252    pub fn normal() -> Self {
253        Self::Oblique(Angle::zero())
254    }
255}
256
257/// A generic value for the `font-size-adjust` property.
258///
259/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop
260#[allow(missing_docs)]
261#[repr(u8)]
262#[derive(
263    Animate,
264    Clone,
265    ComputeSquaredDistance,
266    Copy,
267    Debug,
268    Hash,
269    MallocSizeOf,
270    PartialEq,
271    SpecifiedValueInfo,
272    ToAnimatedValue,
273    ToAnimatedZero,
274    ToComputedValue,
275    ToResolvedValue,
276    ToShmem,
277)]
278pub enum GenericFontSizeAdjust<Factor> {
279    #[animation(error)]
280    None,
281    #[value_info(starts_with_keyword)]
282    ExHeight(Factor),
283    #[value_info(starts_with_keyword)]
284    CapHeight(Factor),
285    #[value_info(starts_with_keyword)]
286    ChWidth(Factor),
287    #[value_info(starts_with_keyword)]
288    IcWidth(Factor),
289    #[value_info(starts_with_keyword)]
290    IcHeight(Factor),
291}
292
293impl<Factor: ToCss> ToCss for GenericFontSizeAdjust<Factor> {
294    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
295    where
296        W: Write,
297    {
298        let (prefix, value) = match self {
299            Self::None => return dest.write_str("none"),
300            Self::ExHeight(v) => ("", v),
301            Self::CapHeight(v) => ("cap-height ", v),
302            Self::ChWidth(v) => ("ch-width ", v),
303            Self::IcWidth(v) => ("ic-width ", v),
304            Self::IcHeight(v) => ("ic-height ", v),
305        };
306
307        dest.write_str(prefix)?;
308        value.to_css(dest)
309    }
310}
311
312impl<Factor: ToTyped> ToTyped for GenericFontSizeAdjust<Factor> {
313    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
314        match self {
315            Self::None => {
316                dest.push(TypedValue::Keyword(KeywordValue(CssString::from("none"))));
317                Ok(())
318            },
319            Self::ExHeight(v) => v.to_typed(dest),
320            _ => Err(()),
321        }
322    }
323}
324
325/// A generic value for the `line-height` property.
326#[derive(
327    Animate,
328    Clone,
329    ComputeSquaredDistance,
330    Copy,
331    Debug,
332    MallocSizeOf,
333    PartialEq,
334    SpecifiedValueInfo,
335    ToAnimatedValue,
336    ToCss,
337    ToShmem,
338    Parse,
339    ToTyped,
340)]
341#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
342#[repr(C, u8)]
343pub enum GenericLineHeight<N, L> {
344    /// `normal`
345    Normal,
346    /// `-moz-block-height`
347    #[cfg(feature = "gecko")]
348    #[parse(condition = "ParserContext::in_ua_sheet")]
349    MozBlockHeight,
350    /// `<number>`
351    Number(N),
352    /// `<length-percentage>`
353    Length(L),
354}
355
356pub use self::GenericLineHeight as LineHeight;
357
358impl<N, L> ToAnimatedZero for LineHeight<N, L> {
359    #[inline]
360    fn to_animated_zero(&self) -> Result<Self, ()> {
361        Err(())
362    }
363}
364
365impl<N, L> LineHeight<N, L> {
366    /// Returns `normal`.
367    #[inline]
368    pub fn normal() -> Self {
369        LineHeight::Normal
370    }
371}