diff --git a/Tests/LibWeb/Text/expected/css/font-face-serialization.txt b/Tests/LibWeb/Text/expected/css/font-face-serialization.txt
new file mode 100644
index 00000000000..1ec6b48e897
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/css/font-face-serialization.txt
@@ -0,0 +1,7 @@
+@font-face { font-family: "a1"; src: local("xyz"); unicode-range: "U+0-10ffff"; }
+@font-face { font-family: "b1"; unicode-range: "U+0-10ffff"; font-feature-settings: "aaaa", "bbbb" 0, "yyyy" 3, "zzzz"; }
+@font-face { font-family: "b2"; unicode-range: "U+0-10ffff"; font-feature-settings: "aaaa" 3; }
+@font-face { font-family: "c1"; unicode-range: "U+0-10ffff"; font-width: 62.5%; }
+@font-face { font-family: "c2"; unicode-range: "U+0-10ffff"; font-width: 50%; }
+@font-face { font-family: "c3"; unicode-range: "U+0-10ffff"; font-width: 62.5%; }
+@font-face { font-family: "c4"; unicode-range: "U+0-10ffff"; font-width: 50%; }
diff --git a/Tests/LibWeb/Text/expected/css/font-face-src-local-serialization.txt b/Tests/LibWeb/Text/expected/css/font-face-src-local-serialization.txt
deleted file mode 100644
index 491578a7596..00000000000
--- a/Tests/LibWeb/Text/expected/css/font-face-src-local-serialization.txt
+++ /dev/null
@@ -1 +0,0 @@
-@font-face { font-family: "a1"; src: local("xyz"); unicode-range: "U+0-10ffff"; }
diff --git a/Tests/LibWeb/Text/input/css/font-face-serialization.html b/Tests/LibWeb/Text/input/css/font-face-serialization.html
new file mode 100644
index 00000000000..34849e46139
--- /dev/null
+++ b/Tests/LibWeb/Text/input/css/font-face-serialization.html
@@ -0,0 +1,30 @@
+
+
diff --git a/Tests/LibWeb/Text/input/css/font-face-src-local-serialization.html b/Tests/LibWeb/Text/input/css/font-face-src-local-serialization.html
deleted file mode 100644
index 6e1505b29ab..00000000000
--- a/Tests/LibWeb/Text/input/css/font-face-src-local-serialization.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
diff --git a/Userland/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp b/Userland/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp
index d1c223f93ce..1067123fe69 100644
--- a/Userland/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp
+++ b/Userland/Libraries/LibWeb/CSS/CSSFontFaceRule.cpp
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include
#include
#include
#include
@@ -92,15 +93,74 @@ String CSSFontFaceRule::serialized() const
// followed by the result of performing serialize a <'font-variant'>,
// followed by the string ";", i.e., SEMICOLON (U+003B).
- // FIXME: 8. If rule’s associated font-feature-settings descriptor is present, a single SPACE (U+0020),
- // followed by the string "font-feature-settings:", followed by a single SPACE (U+0020),
- // followed by the result of performing serialize a <'font-feature-settings'>,
- // followed by the string ";", i.e., SEMICOLON (U+003B).
+ // 8. If rule’s associated font-feature-settings descriptor is present, a single SPACE (U+0020),
+ // followed by the string "font-feature-settings:", followed by a single SPACE (U+0020),
+ // followed by the result of performing serialize a <'font-feature-settings'>,
+ // followed by the string ";", i.e., SEMICOLON (U+003B).
+ if (m_font_face.font_feature_settings().has_value()) {
+ auto const& feature_settings = m_font_face.font_feature_settings().value();
+ builder.append(" font-feature-settings: "sv);
+ // NOTE: We sort the tags during parsing, so they're already in the correct order.
+ bool first = true;
+ for (auto const& [key, value] : feature_settings) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", "sv);
+ }
- // FIXME: 9. If rule’s associated font-stretch descriptor is present, a single SPACE (U+0020),
- // followed by the string "font-stretch:", followed by a single SPACE (U+0020),
- // followed by the result of performing serialize a <'font-stretch'>,
- // followed by the string ";", i.e., SEMICOLON (U+003B).
+ serialize_a_string(builder, key);
+ // NOTE: 1 is the default value, so don't serialize it.
+ if (value != 1)
+ builder.appendff(" {}", value);
+ }
+ builder.append(";"sv);
+ }
+
+ // 9. If rule’s associated font-stretch descriptor is present, a single SPACE (U+0020),
+ // followed by the string "font-stretch:", followed by a single SPACE (U+0020),
+ // followed by the result of performing serialize a <'font-stretch'>,
+ // followed by the string ";", i.e., SEMICOLON (U+003B).
+ // NOTE: font-stretch is now an alias for font-width, so we use that instead.
+ if (m_font_face.width().has_value()) {
+ builder.append(" font-width: "sv);
+ // NOTE: font-width is supposed to always be serialized as a percentage.
+ // Right now, it's stored as a Gfx::FontWidth value, so we have to lossily convert it back.
+ float percentage = 100.0f;
+ switch (m_font_face.width().value()) {
+ case Gfx::FontWidth::UltraCondensed:
+ percentage = 50.0f;
+ break;
+ case Gfx::FontWidth::ExtraCondensed:
+ percentage = 62.5f;
+ break;
+ case Gfx::FontWidth::Condensed:
+ percentage = 75.0f;
+ break;
+ case Gfx::FontWidth::SemiCondensed:
+ percentage = 87.5f;
+ break;
+ case Gfx::FontWidth::Normal:
+ percentage = 100.0f;
+ break;
+ case Gfx::FontWidth::SemiExpanded:
+ percentage = 112.5f;
+ break;
+ case Gfx::FontWidth::Expanded:
+ percentage = 125.0f;
+ break;
+ case Gfx::FontWidth::ExtraExpanded:
+ percentage = 150.0f;
+ break;
+ case Gfx::FontWidth::UltraExpanded:
+ percentage = 200.0f;
+ break;
+ default:
+ break;
+ }
+ builder.appendff("{}%", percentage);
+ builder.append(";"sv);
+ }
// 10. If rule’s associated font-weight descriptor is present, a single SPACE (U+0020),
// followed by the string "font-weight:", followed by a single SPACE (U+0020),