1use cssparser::{
5 AtRuleParser, CowRcStr, DeclarationParser, ParseError, Parser, ParserState,
6 QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser,
7};
8
9use crate::Warning;
10use crate::style::properties::{Family, FontStyle};
11
12use super::declaration::{Spec, at, longhand};
13use super::value::{families, font_style, weight};
14use super::{StyleError, warning};
15
16#[derive(Debug, Clone, PartialEq)]
22pub struct FontFace {
23 pub family: String,
25 pub style: Option<FontStyle>,
27 pub weight: Option<u16>,
29 pub src: Vec<Src>,
31}
32
33#[derive(Debug, Clone, PartialEq)]
35pub enum Src {
36 Url(String),
39 Local(String),
41}
42
43pub(super) fn font_face(input: &mut Parser<'_, '_>, sheet: &str) -> (FontFace, Vec<Warning>) {
45 let mut body = FontFaceBody;
46 let collected: Vec<_> = RuleBodyParser::new(input, &mut body)
47 .map(|result| result.map_err(|(error, _)| error))
48 .collect();
49 let mut face = FontFace {
50 family: String::new(),
51 style: None,
52 weight: None,
53 src: Vec::new(),
54 };
55 let mut warnings = Vec::new();
56 for result in collected {
57 let declarations = match result {
58 Ok(declarations) => declarations,
59 Err(error) => {
60 warnings.push(warning(sheet, &error));
61 continue;
62 }
63 };
64 for declaration in declarations {
65 match declaration {
66 FaceDeclaration::Family(family) => face.family = family,
67 FaceDeclaration::Style(style) => face.style = Some(style),
68 FaceDeclaration::Weight(weight) => face.weight = Some(weight),
69 FaceDeclaration::Src(src) => face.src = src,
70 }
71 }
72 }
73 (face, warnings)
74}
75
76pub(crate) const FONT_FACE_DESCRIPTORS: &[Spec<FaceDeclaration>] = &[
78 Spec {
79 name: "font-family",
80 inherited: false,
81 syntax: "<family-name>",
82 examples: &["\"Author Serif\"", "Author Serif"],
83 read: |name, input| {
84 let family = match families(input)?.first() {
85 Some(Family::Named(family)) => family.clone(),
86 Some(Family::Generic(generic)) => generic.keyword().to_string(),
87 None => {
88 return Err(input.new_custom_error(StyleError::UnsupportedValue(name.clone())));
89 }
90 };
91 Ok(vec![FaceDeclaration::Family(family)])
92 },
93 },
94 Spec {
95 name: "font-style",
96 inherited: false,
97 syntax: "normal | italic | oblique",
98 examples: &["italic"],
99 read: |name, input| longhand(name, input, font_style, FaceDeclaration::Style),
100 },
101 Spec {
102 name: "font-weight",
103 inherited: false,
104 syntax: "normal | bold | <number [1,1000]>",
105 examples: &["bold", "600"],
106 read: |name, input| longhand(name, input, weight, FaceDeclaration::Weight),
107 },
108 Spec {
109 name: "src",
110 inherited: false,
111 syntax: "[ <url> format(<string>)? | local(<string>) ]#",
112 examples: &[
113 "url(fonts/serif.otf)",
114 "url(\"fonts/serif.woff2\") format(\"woff2\")",
115 "local(\"Author Serif\"), url(fonts/serif.otf)",
116 ],
117 read: |_, input| Ok(vec![FaceDeclaration::Src(sources(input)?)]),
118 },
119];
120
121struct FontFaceBody;
123
124pub(crate) enum FaceDeclaration {
126 Family(String),
127 Style(FontStyle),
128 Weight(u16),
129 Src(Vec<Src>),
130}
131
132impl<'i> DeclarationParser<'i> for FontFaceBody {
133 type Declaration = Vec<FaceDeclaration>;
134 type Error = StyleError<'i>;
135
136 fn parse_value<'t>(
137 &mut self,
138 name: CowRcStr<'i>,
139 input: &mut Parser<'i, 't>,
140 start: &ParserState,
141 ) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
142 at(start, |input| {
143 let Some(spec) = Spec::find(FONT_FACE_DESCRIPTORS, &name) else {
144 return Err(input.new_custom_error(StyleError::UnsupportedProperty(name.clone())));
145 };
146 let declarations = spec.read(&name, input)?;
147 input.expect_exhausted()?;
148 Ok(declarations)
149 })(input)
150 }
151}
152
153impl<'i> AtRuleParser<'i> for FontFaceBody {
154 type Prelude = ();
155 type AtRule = Vec<FaceDeclaration>;
156 type Error = StyleError<'i>;
157}
158
159impl<'i> QualifiedRuleParser<'i> for FontFaceBody {
160 type Prelude = ();
161 type QualifiedRule = Vec<FaceDeclaration>;
162 type Error = StyleError<'i>;
163}
164
165impl<'i> RuleBodyItemParser<'i, Vec<FaceDeclaration>, StyleError<'i>> for FontFaceBody {
166 fn parse_declarations(&self) -> bool {
167 true
168 }
169
170 fn parse_qualified(&self) -> bool {
171 false
172 }
173}
174
175fn sources<'i>(input: &mut Parser<'i, '_>) -> Result<Vec<Src>, ParseError<'i, StyleError<'i>>> {
179 input.parse_comma_separated(|input| {
180 if let Ok(url) = input.try_parse(|input| input.expect_url()) {
181 let source = Src::Url(url.as_ref().to_string());
182 let _ = input.try_parse(|input| {
183 input.expect_function_matching("format")?;
184 input.parse_nested_block(|input| {
185 input
186 .expect_string()
187 .map(|_| ())
188 .map_err(ParseError::<StyleError<'_>>::from)
189 })
190 });
191 return Ok(source);
192 }
193 input.expect_function_matching("local")?;
194 let name = input.parse_nested_block(|input| {
195 input
196 .expect_string()
197 .map(|name| name.as_ref().to_string())
198 .map_err(ParseError::from)
199 })?;
200 Ok(Src::Local(name))
201 })
202}