[PATCH 0/2] MR10388: dwrite: Fail loading unsupported COLRv1 fonts.
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/dwrite/dwrite_private.h | 1 + dlls/dwrite/font.c | 7 ++++++- dlls/dwrite/opentype.c | 9 ++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 2d7987d5159..393477e2a5b 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -205,6 +205,7 @@ enum font_flags FONTFACE_NO_KERNING_PAIRS = 0x00000010, FONTFACE_VERTICAL_VARIANTS = 0x00000020, FONTFACE_NO_VERTICAL_VARIANTS = 0x00000040, + FONT_COLR_V1_ONLY = 0x00000080, }; struct dwrite_cmap; diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index f207bade10e..3baa8d2d920 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -4370,7 +4370,12 @@ static HRESULT init_font_data(const struct fontface_desc *desc, DWRITE_FONT_FAMI fontstrings_get_en_string(data->family_names, familyW, ARRAY_SIZE(familyW)); fontstrings_get_en_string(data->names, faceW, ARRAY_SIZE(faceW)); - + if (props.flags & FONT_COLR_V1_ONLY) + { + FIXME("%s/%s COLRv1 is not supported.\n", debugstr_w(familyW), debugstr_w(faceW)); + release_font_data(data); + return DWRITE_E_FILEFORMAT; + } if (family_model == DWRITE_FONT_FAMILY_MODEL_WEIGHT_STRETCH_STYLE && font_apply_differentiation_rules(data, familyW, faceW)) { diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 380732a8c85..c84f9103ea8 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -2091,6 +2091,7 @@ void opentype_get_font_metrics(struct file_stream_desc *stream_desc, DWRITE_FONT void opentype_get_font_properties(const struct file_stream_desc *stream_desc, struct dwrite_font_props *props) { struct dwrite_fonttable os2, head, post, colr, cpal; + const struct colr_header *header; BOOL is_symbol, is_monospaced; opentype_get_font_table(stream_desc, MS_OS2_TAG, &os2); @@ -2213,12 +2214,18 @@ void opentype_get_font_properties(const struct file_stream_desc *stream_desc, st /* FONT_IS_COLORED */ opentype_get_font_table(stream_desc, MS_COLR_TAG, &colr); - if (colr.data) + if (colr.data && (header = table_read_ensure(&colr, 0, sizeof(*header)))) { opentype_get_font_table(stream_desc, MS_CPAL_TAG, &cpal); if (cpal.data) { props->flags |= FONT_IS_COLORED; + /* COLRv1 may still have v0 data for backwards compat, so check num_baseglyph_records. */ + if (header->version && !header->num_baseglyph_records) + { + WARN("COLR version %d.\n", GET_BE_WORD(header->version)); + props->flags |= FONT_COLR_V1_ONLY; + } IDWriteFontFileStream_ReleaseFileFragment(stream_desc->stream, cpal.context); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10388
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/dwrite/analyzer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c index db8e7f8ffd8..f6c2fe8146e 100644 --- a/dlls/dwrite/analyzer.c +++ b/dlls/dwrite/analyzer.c @@ -2553,14 +2553,13 @@ static HRESULT fallback_map_characters(const struct dwrite_fontfallback *fallbac if (SUCCEEDED(create_matching_font(mapping->collection ? mapping->collection : fallback->systemcollection, mapping->families[i], weight, style, stretch, &IID_IDWriteFont3, (void **)&font))) { - if (!(mapped = fallback_font_get_supported_length(font, source, position, mapped))) + if (!(*ret_length = fallback_font_get_supported_length(font, source, position, mapped))) { IDWriteFont3_Release(font); continue; } *ret_font = (IDWriteFont *)font; - *ret_length = mapped; *scale = mapping->scale; return S_OK; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10388
COLRv1 table is now used, e. g., in Noto Color Emoji font (in any available font version I could find). Without the proper support for that (besides format parsing per se, missing interfaces in dwrite, using that in d2d1) the font is loaded but no characters can be rendered. Treating that as non-color font doesn't help, the glyphs, at least the amjority of those, can only be rendered through data from COLR table. Not loading font at least gives a chance for fallback (and fixes emoji rendering in Idle Trillionare game). Another patch is unrelated, currently only the first fallback font can effectively be attempted, the other(s) will get 0 length. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_132913
On Fri Mar 20 08:30:54 2026 +0000, Paul Gofman wrote:
COLRv1 table is now used, e. g., in Noto Color Emoji font (in any available font version I could find). Without the proper support for that (besides format parsing per se, missing interfaces in dwrite, using that in d2d1) the font is loaded but no characters can be rendered. Treating that as non-color font doesn't help, the glyphs, at least the amjority of those, can only be rendered through data from COLR table. Not loading font at least gives a chance for fallback (and fixes emoji rendering in Idle Trillionare game). Another patch is unrelated, currently only the first fallback font can effectively be attempted, the other(s) will get 0 length. Which distro/package is it from? I'd like to try such combination on older Windows, to see what dwrite reports for it. The one hosted here https://fonts.google.com/noto/specimen/Noto+Color+Emoji does have glyf data as well as COLRv1 table, so it should in theory work if regular outlines are reasonable.
But, if your copy only has COLRv1 and nothing else, a better place to reject it would be opentype_ttf_analyzer(). For example checking if COLRv1 is available, but glyf is missing. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_132958
On Fri Mar 20 08:30:53 2026 +0000, Nikolay Sivov wrote:
Which distro/package is it from? I'd like to try such combination on older Windows, to see what dwrite reports for it. The one hosted here https://fonts.google.com/noto/specimen/Noto+Color+Emoji does have glyf data as well as COLRv1 table, so it should in theory work if regular outlines are reasonable. But, if your copy only has COLRv1 and nothing else, a better place to reject it would be opentype_ttf_analyzer(). For example checking if COLRv1 is available, but glyf is missing. There are different versions of this font in packages (different released versions of the font), with or without glyf table, but the result is essentially the same at least WRT the emoji glyphs itself (example symbols are \`\`\`\\xD83D\\xDE04\`\`\` (0x1f604 **:smile:** ), \`\`\`\\xd83d\\xdcb0\`\`\` (0x1f4b0 :moneybag:). I think all of those can be found through the history of font releases here: https://github.com/googlefonts/noto-emoji/releases .
* On Arch with noto-fonts-emoji 1:2.047-1 (NotoColorEmoji.ttf): doesn't have neither COLR nor GLYF, that is bitmap only CBDT font. This is a different story, such fonts also don't render currently, neither through GDI nor dwrite, I only had a brief look and maybe that is about freetype2 usage / requested glyph sizes but I am not fully sure, that needs a better look; * On Fedora google-noto-color-emoji-fonts-20250623-2.fc43.noarch (Noto-COLRv1.ttf): has both colr and glyf tables but the emojis don't render through emoji glyph neither as is nor with disabling FONT_IS_COLORED for it in opentype_get_font_properties(). That's both for dwrite with d2d1 (rendering with dwrite.CreateTextLayout / d2d1.DrawTextLayout) and for the CEF + game which doesn't use d2d, only generates dwrite runs. * The same is with the font linked above (NotoColorEmoji_regular.ttf). So if the font stays with glyf presence this is not solving anything in practice. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133049
On Fri Mar 20 14:37:34 2026 +0000, Paul Gofman wrote:
There are different versions of this font in packages (different released versions of the font), with or without glyf table, but the result is essentially the same at least WRT the emoji glyphs itself (example symbols are \`\`\`\\xD83D\\xDE04\`\`\` (0x1f604 **:smile:** ), \`\`\`\\xd83d\\xdcb0\`\`\` (0x1f4b0 :moneybag:). I think all of those can be found through the history of font releases here: https://github.com/googlefonts/noto-emoji/releases . * On Arch with noto-fonts-emoji 1:2.047-1 (NotoColorEmoji.ttf): doesn't have neither COLR nor GLYF, that is bitmap only CBDT font. This is a different story, such fonts also don't render currently, neither through GDI nor dwrite, I only had a brief look and maybe that is about freetype2 usage / requested glyph sizes but I am not fully sure, that needs a better look; * On Fedora google-noto-color-emoji-fonts-20250623-2.fc43.noarch (Noto-COLRv1.ttf): has both colr and glyf tables but the emojis don't render through emoji glyph neither as is nor with disabling FONT_IS_COLORED for it in opentype_get_font_properties(). That's both for dwrite with d2d1 (rendering with dwrite.CreateTextLayout / d2d1.DrawTextLayout) and for the CEF + game which doesn't use d2d, only generates dwrite runs. * The same is with the font linked above (NotoColorEmoji_regular.ttf). So if the font stays with glyf presence this is not solving anything in practice. I guess glyf table is not supposed to be used directly with COLR fonts? That is referenced from COLR table layers.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133052
On Fri Mar 20 14:41:42 2026 +0000, Paul Gofman wrote:
I guess glyf table is not supposed to be used directly with COLR fonts? That is referenced from COLR table layers. More exactly, there can be glyphs without color which can be rendered through normal glyph run, but the color glyphs cannot be rendered this way at all, not just that they would loose the color.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133054
On Fri Mar 20 14:47:36 2026 +0000, Paul Gofman wrote:
More exactly, there can be glyphs without color which can be rendered through normal glyph run, but the color glyphs cannot be rendered this way at all, not just that they would loose the color. Sure, layers won't be accessible through cmap, but glyphs that are accessible will still work. We could reject anything that has COLRv1 data in it, like you did, but that might reject otherwise usable font. With COLRv0 the logic is simple - for IsColorFont() application should be using Translate*, if application is not aware, it can render as before. Since we don't support COLRv1, is it a problem to simply not report as a color font, and let it render as is?
In practice, it was my impression that nothing is using COLRv0 except for Segoe UI on Windows, and COLRv1 is will always be preferred for newer fonts. Best way would be to test this particular font on older Windows builds without COLRv1 support, like Windows 8/10, and match that. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133055
On Fri Mar 20 15:00:21 2026 +0000, Nikolay Sivov wrote:
Sure, layers won't be accessible through cmap, but glyphs that are accessible will still work. We could reject anything that has COLRv1 data in it, like you did, but that might reject otherwise usable font. With COLRv0 the logic is simple - for IsColorFont() application should be using Translate*, if application is not aware, it can render as before. Since we don't support COLRv1, is it a problem to simply not report as a color font, and let it render as is? In practice, it was my impression that nothing is using COLRv0 except for Segoe UI on Windows, and COLRv1 is will always be preferred for newer fonts. Best way would be to test this particular font on older Windows builds without COLRv1 support, like Windows 8/10, and match that. Segoe UI Emoji is COLRv1 too on up to date Win11.
Well, that won't solve the practical problem which I am trying to solve shorter term. Most likely the games would not render emojis on Win8 / Win10 as well with this COLRv1 font present. While longer term the way forward is proper COLRv1 support and not matching old Windows behaviour? While working on that I had some experiments with translating COLRv1 representation into current COLRv0 (most of characters at least are the same layers with just color and no transparency, just sometimes have nested layers which can be translated; I are also gradients which can be poorly approximated with one color). I am not sure if it worth it, but that can be done rather simple to make the most of such font symbols just render until we have proper v1 support (which is quite some plumbing of course). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133062
On Fri Mar 20 15:09:19 2026 +0000, Paul Gofman wrote:
Segoe UI Emoji is COLRv1 too on up to date Win11. Well, that won't solve the practical problem which I am trying to solve shorter term. Most likely the games would not render emojis on Win8 / Win10 as well with this COLRv1 font present. While longer term the way forward is proper COLRv1 support and not matching old Windows behaviour? While working on that I had some experiments with translating COLRv1 representation into current COLRv0 (most of characters at least are the same layers with just color and no transparency, just sometimes have nested layers which can be translated; I are also gradients which can be poorly approximated with one color). I am not sure if it worth it, but that can be done rather simple to make the most of such font symbols just render until we have proper v1 support (which is quite some plumbing of course). Without COLRv1 support we would be matching old Windows behavior. It's fine to reject anything with COLRv1, but as I said before analyze() is a better place for that.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133063
On Fri Mar 20 15:12:33 2026 +0000, Nikolay Sivov wrote:
Without COLRv1 support we would be matching old Windows behavior. It's fine to reject anything with COLRv1, but as I said before analyze() is a better place for that. By reject anything with COLRv1 do you mean to still refuse loading the font, like patch does?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133069
On Fri Mar 20 15:13:34 2026 +0000, Paul Gofman wrote:
By reject anything with COLRv1 do you mean to still refuse loading the font, like patch does? As for anything else, I yet don't understand the motivation to change anything in this direction, WRT only rendering the glyphs which are not color that already works this way in practice.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133072
By reject anything with COLRv1 do you mean to still refuse loading the font, like patch does?
Yes, by returning supported = FALSE from opentype_analyze_font(). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133074
On Fri Mar 20 15:26:36 2026 +0000, Nikolay Sivov wrote:
By reject anything with COLRv1 do you mean to still refuse loading the font, like patch does? Yes, by returning supported = FALSE from opentype_analyze_font(). Thanks I will update this way.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10388#note_133075
participants (3)
-
Nikolay Sivov (@nsivov) -
Paul Gofman -
Paul Gofman (@gofman)