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