Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions src/AngleSharp.Css.Tests/Declarations/CssFontDescriptorProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
namespace AngleSharp.Css.Tests.Declarations
{
using NUnit.Framework;
using static CssConstructionFunctions;

[TestFixture]
public class CssFontDescriptorPropertyTests
{
[TestCase("size-adjust: 100%", "100%")]
[TestCase("size-adjust: 90.5%", "90.5%")]
[TestCase("size-adjust: 0%", "0%")]
public void SizeAdjustLegalValues(string snippet, string expected)
{
var property = ParseDeclaration(snippet);
Assert.AreEqual("size-adjust", property.Name);
Assert.IsTrue(property.HasValue);
Assert.AreEqual(expected, property.Value);
}

[TestCase("size-adjust: 10px")]
[TestCase("size-adjust: 100")]
[TestCase("size-adjust: auto")]
public void SizeAdjustIllegalValues(string snippet)
{
var property = ParseDeclaration(snippet);
Assert.IsFalse(property.HasValue);
}

[TestCase("ascent-override: 90%", "90%")]
[TestCase("ascent-override: normal", "normal")]
[TestCase("descent-override: 20%", "20%")]
[TestCase("line-gap-override: 0%", "0%")]
public void MetricOverrideLegalValues(string snippet, string expected)
{
var property = ParseDeclaration(snippet);
Assert.IsTrue(property.HasValue);
Assert.AreEqual(expected, property.Value);
}

[TestCase("ascent-override: 10px")]
[TestCase("descent-override: auto")]
[TestCase("line-gap-override: none")]
public void MetricOverrideIllegalValues(string snippet)
{
var property = ParseDeclaration(snippet);
Assert.IsFalse(property.HasValue);
}

[TestCase("font-feature-settings: normal", "normal")]
[TestCase("font-feature-settings: \"liga\"", "\"liga\"")]
[TestCase("font-feature-settings: \"liga\" 1", "\"liga\" 1")]
[TestCase("font-feature-settings: \"kern\" on", "\"kern\" on")]
[TestCase("font-feature-settings: \"kern\" off", "\"kern\" off")]
[TestCase("font-feature-settings: \"liga\" 1, \"kern\" off", "\"liga\" 1, \"kern\" off")]
public void FontFeatureSettingsLegalValues(string snippet, string expected)
{
var property = ParseDeclaration(snippet);
Assert.AreEqual("font-feature-settings", property.Name);
Assert.IsTrue(property.HasValue);
Assert.AreEqual(expected, property.Value);
}

[TestCase("font-feature-settings: 12")]
[TestCase("font-feature-settings: liga")]
[TestCase("font-feature-settings: \"liga\" bogus")]
public void FontFeatureSettingsIllegalValues(string snippet)
{
var property = ParseDeclaration(snippet);
Assert.IsFalse(property.HasValue);
}

[TestCase("font-variation-settings: normal", "normal")]
[TestCase("font-variation-settings: \"wght\" 400", "\"wght\" 400")]
[TestCase("font-variation-settings: \"wght\" 400, \"slnt\" -10", "\"wght\" 400, \"slnt\" -10")]
public void FontVariationSettingsLegalValues(string snippet, string expected)
{
var property = ParseDeclaration(snippet);
Assert.AreEqual("font-variation-settings", property.Name);
Assert.IsTrue(property.HasValue);
Assert.AreEqual(expected, property.Value);
}

[TestCase("font-variation-settings: wght 400")]
[TestCase("font-variation-settings: 400")]
public void FontVariationSettingsIllegalValues(string snippet)
{
var property = ParseDeclaration(snippet);
Assert.IsFalse(property.HasValue);
}
}
}
162 changes: 162 additions & 0 deletions src/AngleSharp.Css.Tests/Rules/FontFaceDescriptors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
namespace AngleSharp.Css.Tests.Rules
{
using AngleSharp.Css.Dom;
using AngleSharp.Css.Parser;
using NUnit.Framework;
using System.Linq;
using static CssConstructionFunctions;

/// <summary>
/// Descriptors inside @font-face used to be limited to a hardcoded set of seven,
/// with everything else dropped silently and without regard to the parser options.
/// These cases pin down that the standard CSS Fonts Level 4 descriptors survive by
/// default and that anything else survives when unknown declarations are included.
/// </summary>
[TestFixture]
public class FontFaceDescriptorTests
{
private static readonly CssParserOptions IncludingUnknown = new() { IsIncludingUnknownDeclarations = true };

[Test]
public void FontFaceKeepsFontDisplay()
{
var sheet = ParseStyleSheet("@font-face { font-display: swap }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("swap", fontface.GetPropertyValue(PropertyNames.FontDisplay));
}

[Test]
public void FontFaceKeepsSizeAdjust()
{
var sheet = ParseStyleSheet("@font-face { size-adjust: 90% }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("90%", fontface.GetPropertyValue(PropertyNames.SizeAdjust));
}

[TestCase("ascent-override", "90%")]
[TestCase("descent-override", "20%")]
[TestCase("line-gap-override", "0%")]
[TestCase("ascent-override", "normal")]
public void FontFaceKeepsMetricOverrides(string name, string value)
{
var sheet = ParseStyleSheet($"@font-face {{ {name}: {value} }}");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual(value, fontface.GetPropertyValue(name));
}

[Test]
public void FontFaceKeepsFontFeatureSettings()
{
var sheet = ParseStyleSheet("@font-face { font-feature-settings: \"liga\" 1, \"kern\" on, \"smcp\" }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("\"liga\" 1, \"kern\" on, \"smcp\"", fontface.GetPropertyValue(PropertyNames.FontFeatureSettings));
}

[Test]
public void FontFaceKeepsFontVariationSettings()
{
var sheet = ParseStyleSheet("@font-face { font-variation-settings: \"wght\" 400, \"slnt\" -10 }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("\"wght\" 400, \"slnt\" -10", fontface.GetPropertyValue(PropertyNames.FontVariationSettings));
}

[Test]
public void FontFaceFeaturesMapToFontFeatureSettings()
{
var sheet = ParseStyleSheet("@font-face { font-feature-settings: \"liga\" 1 }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("\"liga\" 1", fontface.Features);

fontface.Features = "\"kern\" off";
Assert.AreEqual("\"kern\" off", fontface.GetPropertyValue(PropertyNames.FontFeatureSettings));
}

[Test]
public void FontFaceStandardDescriptorsRoundtripViaToCss()
{
var src = "@font-face { font-family: \"FontName\"; src: url(\"https://example.com/font.woff\") format(\"woff\"); font-display: swap; size-adjust: 100% }";
var sheet = ParseStyleSheet(src);
var css = sheet.ToCss();
Assert.That(css, Does.Contain("font-display: swap"));
Assert.That(css, Does.Contain("size-adjust: 100%"));
}

[Test]
public void FontFaceDropsVendorDescriptorByDefault()
{
var sheet = ParseStyleSheet("@font-face { mso-generic-font-family: auto }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual(0, fontface.Length);
}

[Test]
public void FontFaceKeepsVendorDescriptorWhenIncludingUnknownDeclarations()
{
var sheet = ParseStyleSheet("@font-face { mso-generic-font-family: auto }", IncludingUnknown);
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("auto", fontface.GetPropertyValue("mso-generic-font-family"));
Assert.That(sheet.ToCss(), Does.Contain("mso-generic-font-family: auto"));
}

[Test]
public void FontFaceKeepsCustomPropertyWhenIncludingUnknownDeclarations()
{
var sheet = ParseStyleSheet("@font-face { --custom-thing: 12px }", IncludingUnknown);
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("12px", fontface.GetPropertyValue("--custom-thing"));
}

[Test]
public void FontFaceIgnoresInvalidDescriptorValue()
{
var sheet = ParseStyleSheet("@font-face { size-adjust: 10px; ascent-override: bogus }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual(0, fontface.Length);
Assert.That(sheet.ToCss(), Does.Not.Contain("size-adjust"));
}

[Test]
public void FontFaceInvalidValueDoesNotOverwriteValidOne()
{
var sheet = ParseStyleSheet("@font-face { font-weight: 400; font-weight: bogus }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("400", fontface.Weight);
}

[Test]
public void FontFaceValidValueOverwritesEarlierOne()
{
var sheet = ParseStyleSheet("@font-face { font-weight: 400; font-weight: 700 }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
Assert.AreEqual("700", fontface.Weight);
Assert.AreEqual(1, fontface.Length);
}

[Test]
public void CounterStyleKeepsDescriptorsWhenIncludingUnknownDeclarations()
{
var sheet = ParseStyleSheet("@counter-style thumbs { system: cyclic; symbols: \"X\" }", IncludingUnknown);
var rule = (ICssProperties)sheet.Rules[0];
Assert.AreEqual("cyclic", rule.GetPropertyValue("system"));
Assert.AreEqual("\"X\"", rule.GetPropertyValue("symbols"));
}

[Test]
public void ViewportKeepsUnknownDescriptorWhenIncludingUnknownDeclarations()
{
var sheet = ParseStyleSheet("@viewport { width: 100px; foo: bar }", IncludingUnknown);
var rule = (ICssProperties)sheet.Rules[0];
Assert.AreEqual("bar", rule.GetPropertyValue("foo"));
Assert.AreEqual(2, rule.Length);
}

[Test]
public void FontFaceDescriptorsAreEnumerable()
{
var sheet = ParseStyleSheet("@font-face { font-family: \"X\"; font-display: swap; size-adjust: 50% }");
var fontface = (ICssFontFaceRule)sheet.Rules[0];
var names = fontface.Select(m => m.Name).ToArray();
CollectionAssert.AreEquivalent(new[] { "font-family", "font-display", "size-adjust" }, names);
}
}
}
2 changes: 1 addition & 1 deletion src/AngleSharp.Css/BrowsingContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ internal static CssProperty CreateProperty(this IBrowsingContext context, String
private static Boolean AllowsDeclaration(this IBrowsingContext context, DeclarationInfo info) =>
info.Flags != PropertyFlags.Unknown || context.IsAllowingUnknownDeclarations();

private static Boolean IsAllowingUnknownDeclarations(this IBrowsingContext context)
internal static Boolean IsAllowingUnknownDeclarations(this IBrowsingContext context)
{
var parser = context.GetProvider<CssParser>();
return parser?.Options.IsIncludingUnknownDeclarations ?? true;
Expand Down
10 changes: 10 additions & 0 deletions src/AngleSharp.Css/Constants/CssKeywords.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,16 @@ public static class CssKeywords
/// </summary>
public static readonly String Optional = "optional";

/// <summary>
/// The on keyword (font-feature-settings).
/// </summary>
public static readonly String On = "on";

/// <summary>
/// The off keyword (font-feature-settings).
/// </summary>
public static readonly String Off = "off";

/// <summary>
/// The avoid keyword.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/AngleSharp.Css/Constants/InitialValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ static class InitialValues
public static readonly ICssValue FontSynthesisStyleDecl = new CssConstantValue<Object>(CssKeywords.Auto, null);
public static readonly ICssValue FontSynthesisSmallCapsDecl = new CssConstantValue<Object>(CssKeywords.Auto, null);
public static readonly ICssValue FontVariationSettingsDecl = new CssConstantValue<Object>(CssKeywords.Normal, null);
public static readonly ICssValue FontFeatureSettingsDecl = new CssConstantValue<Object>(CssKeywords.Normal, null);
public static readonly ICssValue SizeAdjustDecl = new CssPercentageValue(100.0);
public static readonly ICssValue AscentOverrideDecl = new CssConstantValue<Object>(CssKeywords.Normal, null);
public static readonly ICssValue DescentOverrideDecl = new CssConstantValue<Object>(CssKeywords.Normal, null);
public static readonly ICssValue LineGapOverrideDecl = new CssConstantValue<Object>(CssKeywords.Normal, null);
public static readonly ICssValue AccentColorDecl = new CssConstantValue<Object>(CssKeywords.Auto, null);
public static readonly ICssValue AppearanceDecl = new CssIdentifierValue(CssKeywords.Auto);
public static readonly ICssValue CaretColorDecl = new CssConstantValue<Object>(CssKeywords.Auto, null);
Expand Down
20 changes: 20 additions & 0 deletions src/AngleSharp.Css/Constants/PropertyNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,26 @@ public static class PropertyNames
/// </summary>
public static readonly String FontKerning = "font-kerning";

/// <summary>
/// The size-adjust declaration (@font-face descriptor).
/// </summary>
public static readonly String SizeAdjust = "size-adjust";

/// <summary>
/// The ascent-override declaration (@font-face descriptor).
/// </summary>
public static readonly String AscentOverride = "ascent-override";

/// <summary>
/// The descent-override declaration (@font-face descriptor).
/// </summary>
public static readonly String DescentOverride = "descent-override";

/// <summary>
/// The line-gap-override declaration (@font-face descriptor).
/// </summary>
public static readonly String LineGapOverride = "line-gap-override";

/// <summary>
/// The font-language-override declaration.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions src/AngleSharp.Css/Declarations/AscentOverrideDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace AngleSharp.Css.Declarations
{
using AngleSharp.Css.Dom;
using System;
using static ValueConverters;

static class AscentOverrideDeclaration
{
public static String Name = PropertyNames.AscentOverride;

public static IValueConverter Converter = FontMetricOverrideConverter;

public static ICssValue InitialValue = InitialValues.AscentOverrideDecl;

public static PropertyFlags Flags = PropertyFlags.None;
}
}
17 changes: 17 additions & 0 deletions src/AngleSharp.Css/Declarations/DescentOverrideDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace AngleSharp.Css.Declarations
{
using AngleSharp.Css.Dom;
using System;
using static ValueConverters;

static class DescentOverrideDeclaration
{
public static String Name = PropertyNames.DescentOverride;

public static IValueConverter Converter = FontMetricOverrideConverter;

public static ICssValue InitialValue = InitialValues.DescentOverrideDecl;

public static PropertyFlags Flags = PropertyFlags.None;
}
}
17 changes: 17 additions & 0 deletions src/AngleSharp.Css/Declarations/FontFeatureSettingsDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace AngleSharp.Css.Declarations
{
using AngleSharp.Css.Dom;
using System;
using static ValueConverters;

static class FontFeatureSettingsDeclaration
{
public static String Name = PropertyNames.FontFeatureSettings;

public static IValueConverter Converter = FontFeatureSettingsConverter;

public static ICssValue InitialValue = InitialValues.FontFeatureSettingsDecl;

public static PropertyFlags Flags = PropertyFlags.Inherited;
}
}
17 changes: 17 additions & 0 deletions src/AngleSharp.Css/Declarations/LineGapOverrideDeclaration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace AngleSharp.Css.Declarations
{
using AngleSharp.Css.Dom;
using System;
using static ValueConverters;

static class LineGapOverrideDeclaration
{
public static String Name = PropertyNames.LineGapOverride;

public static IValueConverter Converter = FontMetricOverrideConverter;

public static ICssValue InitialValue = InitialValues.LineGapOverrideDecl;

public static PropertyFlags Flags = PropertyFlags.None;
}
}
Loading
Loading