From 5a1c73d7e26b54565f90512a386eb2deb9448fb7 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 3 Jun 2025 15:48:24 +0100 Subject: [PATCH] LibGfx+LibWeb: Update definitions of supported font formats and features Based very scientifically on what's listed here: https://harfbuzz.github.io/what-does-harfbuzz-do.html I've moved the code into LibGfx because including a HarfBuzz header directly from LibWeb is a little unpleasant. But the Gfx::FontTech enum follows the CSS definitions for font features for simplicity. TrueType collections are supported. SVG and Embedded OpenType are not, but they're not widely supported by other browsers so that's fine. Most of the features are completely supported by HarfBuzz, so we can just return true. Graphite support is optional (and it appears we use a build of HarfBuzz without it) but there's a define we can check. Incremental Font Transfer is a whole separate thing that we definitely don't support yet. --- Libraries/LibGfx/CMakeLists.txt | 1 + Libraries/LibGfx/Font/FontSupport.cpp | 84 +++++++++++++++++++ Libraries/LibGfx/Font/FontSupport.h | 41 +++++++++ Libraries/LibWeb/CSS/FontFace.cpp | 41 +++++---- .../parsing/font-face-src-format.txt | 7 +- .../css-fonts/parsing/font-face-src-tech.txt | 13 ++- 6 files changed, 155 insertions(+), 32 deletions(-) create mode 100644 Libraries/LibGfx/Font/FontSupport.cpp create mode 100644 Libraries/LibGfx/Font/FontSupport.h diff --git a/Libraries/LibGfx/CMakeLists.txt b/Libraries/LibGfx/CMakeLists.txt index e116e137d7c..794271d0145 100644 --- a/Libraries/LibGfx/CMakeLists.txt +++ b/Libraries/LibGfx/CMakeLists.txt @@ -14,6 +14,7 @@ set(SOURCES Font/Font.cpp Font/FontData.cpp Font/FontDatabase.cpp + Font/FontSupport.cpp Font/PathFontProvider.cpp Font/Typeface.cpp Font/TypefaceSkia.cpp diff --git a/Libraries/LibGfx/Font/FontSupport.cpp b/Libraries/LibGfx/Font/FontSupport.cpp new file mode 100644 index 00000000000..f6113dcd10b --- /dev/null +++ b/Libraries/LibGfx/Font/FontSupport.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include + +namespace Gfx { + +bool font_format_is_supported(FontFormat const format) +{ + // FIXME: Determine these automatically. + switch (format) { + case FontFormat::EmbeddedOpenType: + return false; + case FontFormat::OpenType: + return true; + case FontFormat::SVG: + return false; + case FontFormat::TrueType: + return true; + case FontFormat::TrueTypeCollection: + return true; + case FontFormat::WOFF: + return true; + case FontFormat::WOFF2: + return true; + } + + return false; +} + +bool font_tech_is_supported(FontTech const font_tech) +{ + // https://drafts.csswg.org/css-fonts-4/#font-tech-definitions + // FIXME: Determine these automatically. + switch (font_tech) { + case FontTech::FeaturesOpentype: + // GSUB and GPOS, supported by HarfBuzz + return true; + case FontTech::FeaturesAat: + // morx and kerx, supported by HarfBuzz + return true; + case FontTech::FeaturesGraphite: + // Silf, Glat , Gloc , Feat and Sill. HarfBuzz may or may not be built with support for it. +#if HB_HAS_GRAPHITE + return true; +#else + return false; +#endif + case FontTech::Variations: + // avar, cvar, fvar, gvar, HVAR, MVAR, STAT, and VVAR, supported by HarfBuzz + return true; + case FontTech::ColorColrv0: + case FontTech::ColorColrv1: + // COLR, supported by HarfBuzz + return true; + case FontTech::ColorSvg: + // SVG, supported by HarfBuzz + return true; + case FontTech::ColorSbix: + // sbix, supported by HarfBuzz + return true; + case FontTech::ColorCbdt: + // CBDT, supported by HarfBuzz + return true; + case FontTech::Palettes: + // CPAL, supported by HarfBuzz + return true; + case FontTech::Incremental: + // Incremental Font Transfer: https://w3c.github.io/IFT/Overview.html + return false; + // https://drafts.csswg.org/css-fonts-5/#font-tech-definitions + case FontTech::Avar2: + // avar version 2, supported by HarfBuzz + return true; + } + return false; +} + +} diff --git a/Libraries/LibGfx/Font/FontSupport.h b/Libraries/LibGfx/Font/FontSupport.h new file mode 100644 index 00000000000..e9b08b134da --- /dev/null +++ b/Libraries/LibGfx/Font/FontSupport.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Gfx { + +enum class FontFormat : u8 { + EmbeddedOpenType, + OpenType, + SVG, + TrueType, + TrueTypeCollection, + WOFF, + WOFF2, +}; + +enum class FontTech : u8 { + Avar2, + ColorCbdt, + ColorColrv0, + ColorColrv1, + ColorSbix, + ColorSvg, + FeaturesAat, + FeaturesGraphite, + FeaturesOpentype, + Incremental, + Palettes, + Variations, +}; + +bool font_format_is_supported(FontFormat); +bool font_tech_is_supported(FontTech); + +} diff --git a/Libraries/LibWeb/CSS/FontFace.cpp b/Libraries/LibWeb/CSS/FontFace.cpp index 510aab3a76d..914df675291 100644 --- a/Libraries/LibWeb/CSS/FontFace.cpp +++ b/Libraries/LibWeb/CSS/FontFace.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -516,54 +517,52 @@ GC::Ref FontFace::load() bool font_format_is_supported(FlyString const& name) { // https://drafts.csswg.org/css-fonts-4/#font-format-definitions - // FIXME: Determine this automatically somehow? if (name.equals_ignoring_ascii_case("collection"sv)) - return false; + return Gfx::font_format_is_supported(Gfx::FontFormat::TrueTypeCollection); if (name.equals_ignoring_ascii_case("embedded-opentype"sv)) - return false; + return Gfx::font_format_is_supported(Gfx::FontFormat::EmbeddedOpenType); if (name.equals_ignoring_ascii_case("opentype"sv)) - return true; + return Gfx::font_format_is_supported(Gfx::FontFormat::OpenType); if (name.equals_ignoring_ascii_case("svg"sv)) - return false; + return Gfx::font_format_is_supported(Gfx::FontFormat::SVG); if (name.equals_ignoring_ascii_case("truetype"sv)) - return true; + return Gfx::font_format_is_supported(Gfx::FontFormat::TrueType); if (name.equals_ignoring_ascii_case("woff"sv)) - return true; + return Gfx::font_format_is_supported(Gfx::FontFormat::WOFF); if (name.equals_ignoring_ascii_case("woff2"sv)) - return true; + return Gfx::font_format_is_supported(Gfx::FontFormat::WOFF2); return false; } bool font_tech_is_supported(FontTech font_tech) { // https://drafts.csswg.org/css-fonts-4/#font-tech-definitions - // FIXME: Determine this automatically somehow? switch (font_tech) { case FontTech::FeaturesOpentype: - return true; + return Gfx::font_tech_is_supported(Gfx::FontTech::FeaturesOpentype); case FontTech::FeaturesAat: - return false; + return Gfx::font_tech_is_supported(Gfx::FontTech::FeaturesAat); case FontTech::FeaturesGraphite: - return false; + return Gfx::font_tech_is_supported(Gfx::FontTech::FeaturesGraphite); case FontTech::Variations: - return true; + return Gfx::font_tech_is_supported(Gfx::FontTech::Variations); case FontTech::ColorColrv0: - return true; + return Gfx::font_tech_is_supported(Gfx::FontTech::ColorColrv0); case FontTech::ColorColrv1: - return true; + return Gfx::font_tech_is_supported(Gfx::FontTech::ColorColrv1); case FontTech::ColorSvg: - return false; + return Gfx::font_tech_is_supported(Gfx::FontTech::ColorSvg); case FontTech::ColorSbix: - return false; + return Gfx::font_tech_is_supported(Gfx::FontTech::ColorSbix); case FontTech::ColorCbdt: - return false; + return Gfx::font_tech_is_supported(Gfx::FontTech::ColorCbdt); case FontTech::Palettes: - return false; + return Gfx::font_tech_is_supported(Gfx::FontTech::Palettes); case FontTech::Incremental: - return false; + return Gfx::font_tech_is_supported(Gfx::FontTech::Incremental); // https://drafts.csswg.org/css-fonts-5/#font-tech-definitions case FontTech::Avar2: - return false; + return Gfx::font_tech_is_supported(Gfx::FontTech::Avar2); } return false; } diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-format.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-format.txt index 3ac3c5e766d..1ceb38a39ca 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-format.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-format.txt @@ -2,21 +2,20 @@ Harness status: OK Found 35 tests -33 Pass -2 Fail +35 Pass Pass Check that src: url("foo.ttf") is valid Pass Check that src: url("foo.ttf"), url("bar.ttf") is valid Pass Check that src: url("foo.ttf") format() is invalid Pass Check that src: url("foo.ttf") dummy() is invalid Pass Check that src: url("foo.ttf") format("woff") dummy() is invalid Pass Check that src: url("foo.ttf") dummy() format("woff") is invalid -Fail Check that src: url("foo.ttf") format("collection") is valid +Pass Check that src: url("foo.ttf") format("collection") is valid Pass Check that src: url("foo.ttf") format("opentype") is valid Pass Check that src: url("foo.ttf") format("truetype") is valid Pass Check that src: url("foo.ttf") format("woff") is valid Pass Check that src: url("foo.ttf") format("woff2") is valid Pass Check that src: url("foo.ttf") format("opentype", "truetype") is invalid -Fail Check that src: url("foo.ttf") format(collection) is valid +Pass Check that src: url("foo.ttf") format(collection) is valid Pass Check that src: url("foo.ttf") format(opentype) is valid Pass Check that src: url("foo.ttf") format(truetype) is valid Pass Check that src: url("foo.ttf") format(woff) is valid diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-tech.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-tech.txt index ac27aa3efe5..815dd74754b 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-tech.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-fonts/parsing/font-face-src-tech.txt @@ -2,22 +2,21 @@ Harness status: OK Found 39 tests -34 Pass -5 Fail +39 Pass Pass Check that src: url("foo.ttf") is valid Pass Check that src: url("foo.ttf") tech() is invalid Pass Check that src: url("foo.ttf") tech(features-opentype) is valid -Fail Check that src: url("foo.ttf") tech(features-aat) is valid +Pass Check that src: url("foo.ttf") tech(features-aat) is valid Pass Check that src: url("foo.ttf") tech(color-COLRv0) is valid Pass Check that src: url("foo.ttf") tech(color-COLRv1) is valid -Fail Check that src: url("foo.ttf") tech(color-sbix) is valid -Fail Check that src: url("foo.ttf") tech(color-CBDT) is valid +Pass Check that src: url("foo.ttf") tech(color-sbix) is valid +Pass Check that src: url("foo.ttf") tech(color-CBDT) is valid Pass Check that src: url("foo.ttf") tech(variations) is valid -Fail Check that src: url("foo.ttf") tech(palettes) is valid +Pass Check that src: url("foo.ttf") tech(palettes) is valid Pass Check that src: url("foo.ttf") tech("features-opentype") is invalid Pass Check that src: url("foo.ttf") tech("color-COLRv0") is invalid Pass Check that src: url("foo.ttf") tech("variations") is invalid -Fail Check that src: url("foo.ttf") tech(features-opentype, color-COLRv0, variations, palettes) is valid +Pass Check that src: url("foo.ttf") tech(features-opentype, color-COLRv0, variations, palettes) is valid Pass Check that src: url("foo.ttf") tech(features-opentype color-COLRv0 variations palettes) is invalid Pass Check that src: url("foo.ttf") tech(feature-opentype) is invalid Pass Check that src: url("foo.ttf") tech(feature-aat) is invalid