Skip to content

Commit

Permalink
Update supports() to tech() in @font-face rule (#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
CGQAQ authored Aug 22, 2022
1 parent 7dc1a00 commit 6a2bc1d
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 161 deletions.
65 changes: 53 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9442,34 +9442,75 @@ mod tests {
);
minify_test("@font-face {src: local(Test);}", "@font-face{src:local(Test)}");
minify_test("@font-face {src: local(Foo Bar);}", "@font-face{src:local(Foo Bar)}");

minify_test(
"@font-face {src: url(\"test.woff\") format(woff);}",
"@font-face{src:url(test.woff)format(\"woff\")}",
);
minify_test(
"@font-face {src: url(\"test.woff\") format(woff), url(test.ttf) format(truetype);}",
"@font-face{src:url(test.woff)format(\"woff\"),url(test.ttf)format(\"truetype\")}",
"@font-face {src: url(\"test.ttc\") format(collection), url(test.ttf) format(truetype);}",
"@font-face{src:url(test.ttc)format(\"collection\"),url(test.ttf)format(\"truetype\")}",
);
minify_test(
"@font-face {src: url(\"test.otf\") format(opentype) tech(feature-aat);}",
"@font-face{src:url(test.otf)format(\"opentype\")tech(feature-aat)}",
);
minify_test(
"@font-face {src: url(\"test.woff\") format(woff) tech(color-colrv1);}",
"@font-face{src:url(test.woff)format(\"woff\")tech(color-colrv1)}",
);
minify_test(
"@font-face {src: url(\"test.woff2\") format(woff2) tech(variations);}",
"@font-face{src:url(test.woff2)format(\"woff2\")tech(variations)}",
);
minify_test(
"@font-face {src: url(\"test.woff\") format(woff supports features(opentype));}",
"@font-face{src:url(test.woff)format(\"woff\" supports features(opentype))}",
"@font-face {src: url(\"test.woff\") format(woff) tech(palettes);}",
"@font-face{src:url(test.woff)format(\"woff\")tech(palettes)}",
);
// multiple tech
minify_test(
"@font-face {src: url(\"test.woff\") format(woff supports color(COLRv1));}",
"@font-face{src:url(test.woff)format(\"woff\" supports color(colrv1))}",
"@font-face {src: url(\"test.woff\") format(woff) tech(feature-opentype, color-sbix);}",
"@font-face{src:url(test.woff)format(\"woff\")tech(feature-opentype,color-sbix)}",
);
minify_test(
"@font-face {src: url(\"test.woff\") format(woff supports variations);}",
"@font-face{src:url(test.woff)format(\"woff\" supports variations)}",
"@font-face {src: url(\"test.woff\") format(woff) tech(incremental, color-svg, feature-graphite, feature-aat);}",
"@font-face{src:url(test.woff)format(\"woff\")tech(incremental,color-svg,feature-graphite,feature-aat)}",
);
// format() function must precede tech() if both are present
minify_test(
"@font-face {src: url(\"test.woff\") format(woff supports palettes);}",
"@font-face{src:url(test.woff)format(\"woff\" supports palettes)}",
"@font-face {src: url(\"foo.ttf\") format(opentype) tech(color-colrv1);}",
"@font-face{src:url(foo.ttf)format(\"opentype\")tech(color-colrv1)}",
);
// only have tech is valid
minify_test(
"@font-face {src: url(\"test.woff\") format(woff supports features(opentype) color(sbix));}",
"@font-face{src:url(test.woff)format(\"woff\" supports features(opentype) color(sbix))}",
"@font-face {src: url(\"foo.ttf\") tech(color-SVG);}",
"@font-face{src:url(foo.ttf)tech(color-svg)}",
);
// CGQAQ: if tech and format both presence, order is matter, tech before format is invalid
// but now just return raw token, we don't have strict mode yet.
// ref: https://github.com/parcel-bundler/parcel-css/pull/255#issuecomment-1219049998
minify_test(
"@font-face {src: url(\"foo.ttf\") tech(palettes color-colrv0 variations) format(opentype);}",
"@font-face{src:url(foo.ttf) tech(palettes color-colrv0 variations)format(opentype)}",
);
// TODO(CGQAQ): make this test pass when we have strict mode
// ref: https://github.com/web-platform-tests/wpt/blob/9f8a6ccc41aa725e8f51f4f096f686313bb88d8d/css/css-fonts/parsing/font-face-src-tech.html#L45
// error_test(
// "@font-face {src: url(\"foo.ttf\") tech(feature-opentype) format(opentype);}",
// ParserError::AtRuleBodyInvalid,
// );
// error_test(
// "@font-face {src: url(\"foo.ttf\") tech();}",
// ParserError::AtRuleBodyInvalid,
// );
// error_test(
// "@font-face {src: url(\"foo.ttf\") tech(\"feature-opentype\");}",
// ParserError::AtRuleBodyInvalid,
// );
// error_test(
// "@font-face {src: url(\"foo.ttf\") tech(\"color-colrv0\");}",
// ParserError::AtRuleBodyInvalid,
// );
minify_test(
"@font-face {src: local(\"\") url(\"test.woff\");}",
"@font-face{src:local(\"\")url(test.woff)}",
Expand Down
212 changes: 63 additions & 149 deletions src/rules/font_face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,15 @@ pub enum Source<'i> {

impl<'i> Parse<'i> for Source<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
if let Ok(url) = input.try_parse(UrlSource::parse) {
return Ok(Source::Url(url));
match input.try_parse(UrlSource::parse) {
Ok(url) => return Ok(Source::Url(url)),
e @ Err(ParseError {
kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid),
..
}) => {
return Err(e.err().unwrap());
}
_ => {}
}

input.expect_function_matching("local")?;
Expand Down Expand Up @@ -104,20 +111,28 @@ pub struct UrlSource<'i> {
pub url: Url<'i>,
/// Optional `format()` function.
#[cfg_attr(feature = "serde", serde(borrow))]
pub format: Option<Format<'i>>,
pub format: Option<FontFormat<'i>>,
/// Optional `tech()` function.
pub tech: Vec<FontTechnology>,
}

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

let format = if input.try_parse(|input| input.expect_function_matching("format")).is_ok() {
Some(input.parse_nested_block(Format::parse)?)
Some(input.parse_nested_block(FontFormat::parse)?)
} else {
None
};

Ok(UrlSource { url, format })
let tech = if input.try_parse(|input| input.expect_function_matching("tech")).is_ok() {
input.parse_nested_block(Vec::<FontTechnology>::parse)?
} else {
vec![]
};

Ok(UrlSource { url, format, tech })
}
}

Expand All @@ -133,57 +148,12 @@ impl<'i> ToCss for UrlSource<'i> {
format.to_css(dest)?;
dest.write_char(')')?;
}
Ok(())
}
}

/// The `format()` function within the [src](https://drafts.csswg.org/css-fonts/#src-desc)
/// property of an `@font-face` rule.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Format<'i> {
/// A font format name.
#[cfg_attr(feature = "serde", serde(borrow))]
pub format: FontFormat<'i>,
/// The `supports()` function.
// TODO: did this get renamed to `tech()`?
pub supports: Vec<FontTechnology>,
}

impl<'i> Parse<'i> for Format<'i> {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
let format = FontFormat::parse(input)?;
let mut supports = vec![];
if input.try_parse(|input| input.expect_ident_matching("supports")).is_ok() {
loop {
if let Ok(technology) = input.try_parse(FontTechnology::parse) {
supports.push(technology)
} else {
break;
}
}
}
Ok(Format { format, supports })
}
}

impl<'i> ToCss for Format<'i> {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
self.format.to_css(dest)?;
if !self.supports.is_empty() {
dest.write_str(" supports ")?;
let mut first = true;
for technology in &self.supports {
if first {
first = false;
} else {
dest.write_char(' ')?;
}
technology.to_css(dest)?;
}
if !self.tech.is_empty() {
dest.whitespace()?;
dest.write_str("tech(")?;
self.tech.to_css(dest)?;
dest.write_char(')')?;
}
Ok(())
}
Expand All @@ -199,17 +169,18 @@ impl<'i> ToCss for Format<'i> {
serde(tag = "type", content = "value", rename_all = "kebab-case")
)]
pub enum FontFormat<'i> {
/// A WOFF font.
/// [src](https://drafts.csswg.org/css-fonts/#font-format-definitions)
/// A WOFF 1.0 font.
WOFF,
/// A WOFF v2 font.
/// A WOFF 2.0 font.
WOFF2,
/// A TrueType font.
TrueType,
/// An OpenType font.
OpenType,
/// An Embedded OpenType (.eot) font.
EmbeddedOpenType,
/// A font collection.
/// OpenType Collection.
Collection,
/// An SVG font.
SVG,
Expand Down Expand Up @@ -258,103 +229,46 @@ impl<'i> ToCss for FontFormat<'i> {
}

enum_property! {
/// A font feature tech descriptor in the `supports()`function of the
/// [src](https://drafts.csswg.org/css-fonts/#src-desc)
/// property of an `@font-face` rule.
pub enum FontFeatureTechnology {
/// Supports OpenType features.
OpenType,
/// Supports Apple Advanced Typography features.
AAT,
/// Supports Graphite features.
Graphite,
}
}

enum_property! {
/// A color font tech descriptor in the `supports()`function of the
/// A font format keyword in the `format()` function of the the
/// [src](https://drafts.csswg.org/css-fonts/#src-desc)
/// property of an `@font-face` rule.
pub enum ColorFontTechnology {
pub enum FontTechnology {
/// A font feature tech descriptor in the `tech()`function of the
/// [src](https://drafts.csswg.org/css-fonts/#font-feature-tech-values)
/// property of an `@font-face` rule.
/// Supports OpenType Features.
/// https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist
"feature-opentype": FeatureOpentype,
/// Supports Apple Advanced Typography Font Features.
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html
"feature-aat": FeatureAat,
/// Supports Graphite Table Format.
/// https://scripts.sil.org/cms/scripts/render_download.php?site_id=nrsi&format=file&media_id=GraphiteBinaryFormat_3_0&filename=GraphiteBinaryFormat_3_0.pdf
"feature-graphite": FeatureGraphite,

/// A color font tech descriptor in the `tech()`function of the
/// [src](https://drafts.csswg.org/css-fonts/#src-desc)
/// property of an `@font-face` rule.
/// Supports the `COLR` v0 table.
COLRv0,
"color-colrv0": ColorCOLRv0,
/// Supports the `COLR` v1 table.
COLRv1,
/// Supports SVG glyphs.
SVG,
"color-colrv1": ColorCOLRv1,
/// Supports the `SVG` table.
"color-svg": ColorSVG,
/// Supports the `sbix` table.
SBIX,
"color-sbix": ColorSbix,
/// Supports the `CBDT` table.
CBDT,
}
}

/// A font technology descriptor in the `supports()`function of the
/// [src](https://drafts.csswg.org/css-fonts/#src-desc)
/// property of an `@font-face` rule.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type", content = "value", rename_all = "kebab-case")
)]
pub enum FontTechnology {
/// Supports font features.
Features(FontFeatureTechnology),
/// Supports variations.
Variations,
/// Supports color glyphs.
Color(ColorFontTechnology),
/// Supports color palettes.
Palettes,
}

impl<'i> Parse<'i> for FontTechnology {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
let location = input.current_source_location();
match input.next()? {
Token::Function(f) => {
match_ignore_ascii_case! { &f,
"features" => Ok(FontTechnology::Features(input.parse_nested_block(FontFeatureTechnology::parse)?)),
"color" => Ok(FontTechnology::Color(input.parse_nested_block(ColorFontTechnology::parse)?)),
_ => Err(location.new_unexpected_token_error(
cssparser::Token::Ident(f.clone())
))
}
}
Token::Ident(ident) => {
match_ignore_ascii_case! { &ident,
"variations" => Ok(FontTechnology::Variations),
"palettes" => Ok(FontTechnology::Palettes),
_ => Err(location.new_unexpected_token_error(
cssparser::Token::Ident(ident.clone())
))
}
}
tok => Err(location.new_unexpected_token_error(tok.clone())),
}
}
}

impl ToCss for FontTechnology {
fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
FontTechnology::Features(f) => {
dest.write_str("features(")?;
f.to_css(dest)?;
dest.write_char(')')
}
FontTechnology::Color(c) => {
dest.write_str("color(")?;
c.to_css(dest)?;
dest.write_char(')')
}
FontTechnology::Variations => dest.write_str("variations"),
FontTechnology::Palettes => dest.write_str("palettes"),
}
"color-cbdt": ColorCBDT,

/// Supports Variations
/// The variations tech refers to the support of font variations
"variations": Variations,
/// Supports Palettes
/// The palettes tech refers to support for font palettes
"palettes": Palettes,
/// Supports Incremental
/// The incremental tech refers to client support for incremental font loading, using either the range-request or the patch-subset method
"incremental": Incremental,
}
}

Expand Down

0 comments on commit 6a2bc1d

Please sign in to comment.