Skip to content

Commit 6a2bc1d

Browse files
authored
Update supports() to tech() in @font-face rule (parcel-bundler#255)
1 parent 7dc1a00 commit 6a2bc1d

File tree

2 files changed

+116
-161
lines changed

2 files changed

+116
-161
lines changed

src/lib.rs

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9442,34 +9442,75 @@ mod tests {
94429442
);
94439443
minify_test("@font-face {src: local(Test);}", "@font-face{src:local(Test)}");
94449444
minify_test("@font-face {src: local(Foo Bar);}", "@font-face{src:local(Foo Bar)}");
9445+
94459446
minify_test(
94469447
"@font-face {src: url(\"test.woff\") format(woff);}",
94479448
"@font-face{src:url(test.woff)format(\"woff\")}",
94489449
);
94499450
minify_test(
9450-
"@font-face {src: url(\"test.woff\") format(woff), url(test.ttf) format(truetype);}",
9451-
"@font-face{src:url(test.woff)format(\"woff\"),url(test.ttf)format(\"truetype\")}",
9451+
"@font-face {src: url(\"test.ttc\") format(collection), url(test.ttf) format(truetype);}",
9452+
"@font-face{src:url(test.ttc)format(\"collection\"),url(test.ttf)format(\"truetype\")}",
9453+
);
9454+
minify_test(
9455+
"@font-face {src: url(\"test.otf\") format(opentype) tech(feature-aat);}",
9456+
"@font-face{src:url(test.otf)format(\"opentype\")tech(feature-aat)}",
9457+
);
9458+
minify_test(
9459+
"@font-face {src: url(\"test.woff\") format(woff) tech(color-colrv1);}",
9460+
"@font-face{src:url(test.woff)format(\"woff\")tech(color-colrv1)}",
9461+
);
9462+
minify_test(
9463+
"@font-face {src: url(\"test.woff2\") format(woff2) tech(variations);}",
9464+
"@font-face{src:url(test.woff2)format(\"woff2\")tech(variations)}",
94529465
);
94539466
minify_test(
9454-
"@font-face {src: url(\"test.woff\") format(woff supports features(opentype));}",
9455-
"@font-face{src:url(test.woff)format(\"woff\" supports features(opentype))}",
9467+
"@font-face {src: url(\"test.woff\") format(woff) tech(palettes);}",
9468+
"@font-face{src:url(test.woff)format(\"woff\")tech(palettes)}",
94569469
);
9470+
// multiple tech
94579471
minify_test(
9458-
"@font-face {src: url(\"test.woff\") format(woff supports color(COLRv1));}",
9459-
"@font-face{src:url(test.woff)format(\"woff\" supports color(colrv1))}",
9472+
"@font-face {src: url(\"test.woff\") format(woff) tech(feature-opentype, color-sbix);}",
9473+
"@font-face{src:url(test.woff)format(\"woff\")tech(feature-opentype,color-sbix)}",
94609474
);
94619475
minify_test(
9462-
"@font-face {src: url(\"test.woff\") format(woff supports variations);}",
9463-
"@font-face{src:url(test.woff)format(\"woff\" supports variations)}",
9476+
"@font-face {src: url(\"test.woff\") format(woff) tech(incremental, color-svg, feature-graphite, feature-aat);}",
9477+
"@font-face{src:url(test.woff)format(\"woff\")tech(incremental,color-svg,feature-graphite,feature-aat)}",
94649478
);
9479+
// format() function must precede tech() if both are present
94659480
minify_test(
9466-
"@font-face {src: url(\"test.woff\") format(woff supports palettes);}",
9467-
"@font-face{src:url(test.woff)format(\"woff\" supports palettes)}",
9481+
"@font-face {src: url(\"foo.ttf\") format(opentype) tech(color-colrv1);}",
9482+
"@font-face{src:url(foo.ttf)format(\"opentype\")tech(color-colrv1)}",
94689483
);
9484+
// only have tech is valid
94699485
minify_test(
9470-
"@font-face {src: url(\"test.woff\") format(woff supports features(opentype) color(sbix));}",
9471-
"@font-face{src:url(test.woff)format(\"woff\" supports features(opentype) color(sbix))}",
9486+
"@font-face {src: url(\"foo.ttf\") tech(color-SVG);}",
9487+
"@font-face{src:url(foo.ttf)tech(color-svg)}",
94729488
);
9489+
// CGQAQ: if tech and format both presence, order is matter, tech before format is invalid
9490+
// but now just return raw token, we don't have strict mode yet.
9491+
// ref: https://github.com/parcel-bundler/parcel-css/pull/255#issuecomment-1219049998
9492+
minify_test(
9493+
"@font-face {src: url(\"foo.ttf\") tech(palettes color-colrv0 variations) format(opentype);}",
9494+
"@font-face{src:url(foo.ttf) tech(palettes color-colrv0 variations)format(opentype)}",
9495+
);
9496+
// TODO(CGQAQ): make this test pass when we have strict mode
9497+
// ref: https://github.com/web-platform-tests/wpt/blob/9f8a6ccc41aa725e8f51f4f096f686313bb88d8d/css/css-fonts/parsing/font-face-src-tech.html#L45
9498+
// error_test(
9499+
// "@font-face {src: url(\"foo.ttf\") tech(feature-opentype) format(opentype);}",
9500+
// ParserError::AtRuleBodyInvalid,
9501+
// );
9502+
// error_test(
9503+
// "@font-face {src: url(\"foo.ttf\") tech();}",
9504+
// ParserError::AtRuleBodyInvalid,
9505+
// );
9506+
// error_test(
9507+
// "@font-face {src: url(\"foo.ttf\") tech(\"feature-opentype\");}",
9508+
// ParserError::AtRuleBodyInvalid,
9509+
// );
9510+
// error_test(
9511+
// "@font-face {src: url(\"foo.ttf\") tech(\"color-colrv0\");}",
9512+
// ParserError::AtRuleBodyInvalid,
9513+
// );
94739514
minify_test(
94749515
"@font-face {src: local(\"\") url(\"test.woff\");}",
94759516
"@font-face{src:local(\"\")url(test.woff)}",

src/rules/font_face.rs

Lines changed: 63 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,15 @@ pub enum Source<'i> {
6969

7070
impl<'i> Parse<'i> for Source<'i> {
7171
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
72-
if let Ok(url) = input.try_parse(UrlSource::parse) {
73-
return Ok(Source::Url(url));
72+
match input.try_parse(UrlSource::parse) {
73+
Ok(url) => return Ok(Source::Url(url)),
74+
e @ Err(ParseError {
75+
kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid),
76+
..
77+
}) => {
78+
return Err(e.err().unwrap());
79+
}
80+
_ => {}
7481
}
7582

7683
input.expect_function_matching("local")?;
@@ -104,20 +111,28 @@ pub struct UrlSource<'i> {
104111
pub url: Url<'i>,
105112
/// Optional `format()` function.
106113
#[cfg_attr(feature = "serde", serde(borrow))]
107-
pub format: Option<Format<'i>>,
114+
pub format: Option<FontFormat<'i>>,
115+
/// Optional `tech()` function.
116+
pub tech: Vec<FontTechnology>,
108117
}
109118

110119
impl<'i> Parse<'i> for UrlSource<'i> {
111120
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
112121
let url = Url::parse(input)?;
113122

114123
let format = if input.try_parse(|input| input.expect_function_matching("format")).is_ok() {
115-
Some(input.parse_nested_block(Format::parse)?)
124+
Some(input.parse_nested_block(FontFormat::parse)?)
116125
} else {
117126
None
118127
};
119128

120-
Ok(UrlSource { url, format })
129+
let tech = if input.try_parse(|input| input.expect_function_matching("tech")).is_ok() {
130+
input.parse_nested_block(Vec::<FontTechnology>::parse)?
131+
} else {
132+
vec![]
133+
};
134+
135+
Ok(UrlSource { url, format, tech })
121136
}
122137
}
123138

@@ -133,57 +148,12 @@ impl<'i> ToCss for UrlSource<'i> {
133148
format.to_css(dest)?;
134149
dest.write_char(')')?;
135150
}
136-
Ok(())
137-
}
138-
}
139-
140-
/// The `format()` function within the [src](https://drafts.csswg.org/css-fonts/#src-desc)
141-
/// property of an `@font-face` rule.
142-
#[derive(Debug, Clone, PartialEq)]
143-
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
144-
pub struct Format<'i> {
145-
/// A font format name.
146-
#[cfg_attr(feature = "serde", serde(borrow))]
147-
pub format: FontFormat<'i>,
148-
/// The `supports()` function.
149-
// TODO: did this get renamed to `tech()`?
150-
pub supports: Vec<FontTechnology>,
151-
}
152151

153-
impl<'i> Parse<'i> for Format<'i> {
154-
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
155-
let format = FontFormat::parse(input)?;
156-
let mut supports = vec![];
157-
if input.try_parse(|input| input.expect_ident_matching("supports")).is_ok() {
158-
loop {
159-
if let Ok(technology) = input.try_parse(FontTechnology::parse) {
160-
supports.push(technology)
161-
} else {
162-
break;
163-
}
164-
}
165-
}
166-
Ok(Format { format, supports })
167-
}
168-
}
169-
170-
impl<'i> ToCss for Format<'i> {
171-
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
172-
where
173-
W: std::fmt::Write,
174-
{
175-
self.format.to_css(dest)?;
176-
if !self.supports.is_empty() {
177-
dest.write_str(" supports ")?;
178-
let mut first = true;
179-
for technology in &self.supports {
180-
if first {
181-
first = false;
182-
} else {
183-
dest.write_char(' ')?;
184-
}
185-
technology.to_css(dest)?;
186-
}
152+
if !self.tech.is_empty() {
153+
dest.whitespace()?;
154+
dest.write_str("tech(")?;
155+
self.tech.to_css(dest)?;
156+
dest.write_char(')')?;
187157
}
188158
Ok(())
189159
}
@@ -199,17 +169,18 @@ impl<'i> ToCss for Format<'i> {
199169
serde(tag = "type", content = "value", rename_all = "kebab-case")
200170
)]
201171
pub enum FontFormat<'i> {
202-
/// A WOFF font.
172+
/// [src](https://drafts.csswg.org/css-fonts/#font-format-definitions)
173+
/// A WOFF 1.0 font.
203174
WOFF,
204-
/// A WOFF v2 font.
175+
/// A WOFF 2.0 font.
205176
WOFF2,
206177
/// A TrueType font.
207178
TrueType,
208179
/// An OpenType font.
209180
OpenType,
210181
/// An Embedded OpenType (.eot) font.
211182
EmbeddedOpenType,
212-
/// A font collection.
183+
/// OpenType Collection.
213184
Collection,
214185
/// An SVG font.
215186
SVG,
@@ -258,103 +229,46 @@ impl<'i> ToCss for FontFormat<'i> {
258229
}
259230

260231
enum_property! {
261-
/// A font feature tech descriptor in the `supports()`function of the
262-
/// [src](https://drafts.csswg.org/css-fonts/#src-desc)
263-
/// property of an `@font-face` rule.
264-
pub enum FontFeatureTechnology {
265-
/// Supports OpenType features.
266-
OpenType,
267-
/// Supports Apple Advanced Typography features.
268-
AAT,
269-
/// Supports Graphite features.
270-
Graphite,
271-
}
272-
}
273-
274-
enum_property! {
275-
/// A color font tech descriptor in the `supports()`function of the
232+
/// A font format keyword in the `format()` function of the the
276233
/// [src](https://drafts.csswg.org/css-fonts/#src-desc)
277234
/// property of an `@font-face` rule.
278-
pub enum ColorFontTechnology {
235+
pub enum FontTechnology {
236+
/// A font feature tech descriptor in the `tech()`function of the
237+
/// [src](https://drafts.csswg.org/css-fonts/#font-feature-tech-values)
238+
/// property of an `@font-face` rule.
239+
/// Supports OpenType Features.
240+
/// https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist
241+
"feature-opentype": FeatureOpentype,
242+
/// Supports Apple Advanced Typography Font Features.
243+
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html
244+
"feature-aat": FeatureAat,
245+
/// Supports Graphite Table Format.
246+
/// https://scripts.sil.org/cms/scripts/render_download.php?site_id=nrsi&format=file&media_id=GraphiteBinaryFormat_3_0&filename=GraphiteBinaryFormat_3_0.pdf
247+
"feature-graphite": FeatureGraphite,
248+
249+
/// A color font tech descriptor in the `tech()`function of the
250+
/// [src](https://drafts.csswg.org/css-fonts/#src-desc)
251+
/// property of an `@font-face` rule.
279252
/// Supports the `COLR` v0 table.
280-
COLRv0,
253+
"color-colrv0": ColorCOLRv0,
281254
/// Supports the `COLR` v1 table.
282-
COLRv1,
283-
/// Supports SVG glyphs.
284-
SVG,
255+
"color-colrv1": ColorCOLRv1,
256+
/// Supports the `SVG` table.
257+
"color-svg": ColorSVG,
285258
/// Supports the `sbix` table.
286-
SBIX,
259+
"color-sbix": ColorSbix,
287260
/// Supports the `CBDT` table.
288-
CBDT,
289-
}
290-
}
291-
292-
/// A font technology descriptor in the `supports()`function of the
293-
/// [src](https://drafts.csswg.org/css-fonts/#src-desc)
294-
/// property of an `@font-face` rule.
295-
#[derive(Debug, Clone, PartialEq)]
296-
#[cfg_attr(
297-
feature = "serde",
298-
derive(serde::Serialize, serde::Deserialize),
299-
serde(tag = "type", content = "value", rename_all = "kebab-case")
300-
)]
301-
pub enum FontTechnology {
302-
/// Supports font features.
303-
Features(FontFeatureTechnology),
304-
/// Supports variations.
305-
Variations,
306-
/// Supports color glyphs.
307-
Color(ColorFontTechnology),
308-
/// Supports color palettes.
309-
Palettes,
310-
}
311-
312-
impl<'i> Parse<'i> for FontTechnology {
313-
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
314-
let location = input.current_source_location();
315-
match input.next()? {
316-
Token::Function(f) => {
317-
match_ignore_ascii_case! { &f,
318-
"features" => Ok(FontTechnology::Features(input.parse_nested_block(FontFeatureTechnology::parse)?)),
319-
"color" => Ok(FontTechnology::Color(input.parse_nested_block(ColorFontTechnology::parse)?)),
320-
_ => Err(location.new_unexpected_token_error(
321-
cssparser::Token::Ident(f.clone())
322-
))
323-
}
324-
}
325-
Token::Ident(ident) => {
326-
match_ignore_ascii_case! { &ident,
327-
"variations" => Ok(FontTechnology::Variations),
328-
"palettes" => Ok(FontTechnology::Palettes),
329-
_ => Err(location.new_unexpected_token_error(
330-
cssparser::Token::Ident(ident.clone())
331-
))
332-
}
333-
}
334-
tok => Err(location.new_unexpected_token_error(tok.clone())),
335-
}
336-
}
337-
}
338-
339-
impl ToCss for FontTechnology {
340-
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
341-
where
342-
W: std::fmt::Write,
343-
{
344-
match self {
345-
FontTechnology::Features(f) => {
346-
dest.write_str("features(")?;
347-
f.to_css(dest)?;
348-
dest.write_char(')')
349-
}
350-
FontTechnology::Color(c) => {
351-
dest.write_str("color(")?;
352-
c.to_css(dest)?;
353-
dest.write_char(')')
354-
}
355-
FontTechnology::Variations => dest.write_str("variations"),
356-
FontTechnology::Palettes => dest.write_str("palettes"),
357-
}
261+
"color-cbdt": ColorCBDT,
262+
263+
/// Supports Variations
264+
/// The variations tech refers to the support of font variations
265+
"variations": Variations,
266+
/// Supports Palettes
267+
/// The palettes tech refers to support for font palettes
268+
"palettes": Palettes,
269+
/// Supports Incremental
270+
/// The incremental tech refers to client support for incremental font loading, using either the range-request or the patch-subset method
271+
"incremental": Incremental,
358272
}
359273
}
360274

0 commit comments

Comments
 (0)