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