From caf0b9c082488f87abfddb7a37a3e19e64e4b514 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 4 Nov 2020 09:15:06 +0100 Subject: [PATCH] gdi32: Add some helper functions for font matching. Signed-off-by: Alexandre Julliard --- dlls/gdi32/font.c | 142 +++++++++++++++++++----- dlls/gdi32/freetype.c | 232 ++++++--------------------------------- dlls/gdi32/gdi_private.h | 8 +- 3 files changed, 151 insertions(+), 231 deletions(-) diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index ef552bfc896..e28471cb2eb 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -554,7 +554,7 @@ static void load_gdi_font_subst(void) /* font families */ -struct list font_list = LIST_INIT(font_list); +static struct list font_list = LIST_INIT(font_list); static struct gdi_font_family *create_family( const WCHAR *name, const WCHAR *second_name ) { @@ -592,7 +592,7 @@ static struct gdi_font_family *find_family_from_name( const WCHAR *name ) return NULL; } -struct gdi_font_family *find_family_from_any_name( const WCHAR *name ) +static struct gdi_font_family *find_family_from_any_name( const WCHAR *name ) { struct gdi_font_family *family; @@ -1160,8 +1160,8 @@ struct gdi_font_link *find_gdi_font_link( const WCHAR *name ) return NULL; } -struct gdi_font_family *find_family_from_font_links( const WCHAR *name, const WCHAR *subst, - FONTSIGNATURE fs ) +static struct gdi_font_family *find_family_from_font_links( const WCHAR *name, const WCHAR *subst, + FONTSIGNATURE fs ) { struct gdi_font_link *link; struct gdi_font_link_entry *entry; @@ -1391,6 +1391,114 @@ static void load_system_links(void) } } +/* font matching */ + +static BOOL can_select_face( const struct gdi_font_face *face, FONTSIGNATURE fs, BOOL can_use_bitmap ) +{ + struct gdi_font_link *font_link; + + if (!face->scalable && !can_use_bitmap) return FALSE; + if (!fs.fsCsb[0]) return TRUE; + if (fs.fsCsb[0] & face->fs.fsCsb[0]) return TRUE; + if (!(font_link = find_gdi_font_link( face->family->family_name ))) return FALSE; + if (fs.fsCsb[0] & font_link->fs.fsCsb[0]) return TRUE; + return FALSE; +} + +static struct gdi_font_face *find_best_matching_face( const struct gdi_font_family *family, + const LOGFONTW *lf, FONTSIGNATURE fs, + BOOL can_use_bitmap ) +{ + struct gdi_font_face *face = NULL, *best = NULL, *best_bitmap = NULL; + unsigned int best_score = 4; + int best_diff = 0; + int it = !!lf->lfItalic; + int bd = lf->lfWeight > 550; + int height = lf->lfHeight; + + LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry ) + { + int italic = !!(face->ntmFlags & NTM_ITALIC); + int bold = !!(face->ntmFlags & NTM_BOLD); + int score = (italic ^ it) + (bold ^ bd); + + if (!can_select_face( face, fs, can_use_bitmap )) continue; + if (score > best_score) continue; + TRACE( "(it=%d, bd=%d) is selected for (it=%d, bd=%d)\n", italic, bold, it, bd ); + best_score = score; + best = face; + if (best->scalable && best_score == 0) break; + if (!best->scalable) + { + int diff; + if (height > 0) + diff = height - (signed int)best->size.height; + else + diff = -height - ((signed int)best->size.height - best->size.internal_leading); + if (!best_bitmap || + (best_diff > 0 && diff >= 0 && diff < best_diff) || + (best_diff < 0 && diff > best_diff)) + { + TRACE( "%d is better for %d diff was %d\n", best->size.height, height, best_diff ); + best_diff = diff; + best_bitmap = best; + if (best_score == 0 && best_diff == 0) break; + } + } + } + if (!best) return NULL; + return best->scalable ? best : best_bitmap; +} + +struct gdi_font_face *find_matching_face_by_name( const WCHAR *name, const WCHAR *subst, const LOGFONTW *lf, + FONTSIGNATURE fs, BOOL can_use_bitmap ) +{ + struct gdi_font_family *family; + struct gdi_font_face *face; + + family = find_family_from_any_name( name ); + if (family && (face = find_best_matching_face( family, lf, fs, can_use_bitmap ))) return face; + if (subst) + { + family = find_family_from_any_name( subst ); + if (family && (face = find_best_matching_face( family, lf, fs, can_use_bitmap ))) return face; + } + + /* search by full face name */ + LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, entry ) + LIST_FOR_EACH_ENTRY( face, get_family_face_list(family), struct gdi_font_face, entry ) + if (!strncmpiW( face->full_name, name, LF_FACESIZE - 1 ) && + can_select_face( face, fs, can_use_bitmap )) + return face; + + if ((family = find_family_from_font_links( name, subst, fs ))) + { + if ((face = find_best_matching_face( family, lf, fs, can_use_bitmap ))) return face; + } + return NULL; +} + +struct gdi_font_face *find_any_face( const LOGFONTW *lf, FONTSIGNATURE fs, + BOOL can_use_bitmap, BOOL want_vertical ) +{ + struct gdi_font_family *family; + struct gdi_font_face *face; + + /* first try only scalable */ + LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, entry ) + { + if ((family->family_name[0] == '@') == !want_vertical) continue; + if ((face = find_best_matching_face( family, lf, fs, FALSE ))) return face; + } + if (!can_use_bitmap) return NULL; + LIST_FOR_EACH_ENTRY( family, &font_list, struct gdi_font_family, entry ) + { + if ((family->family_name[0] == '@') == !want_vertical) continue; + if ((face = find_best_matching_face( family, lf, fs, can_use_bitmap ))) return face; + } + return NULL; +} + /* realized font objects */ #define FIRST_FONT_HANDLE 1 @@ -1927,31 +2035,13 @@ static UINT get_GSUB_vert_glyph( struct gdi_font *font, UINT glyph ) static void add_child_font( struct gdi_font *font, const WCHAR *family_name ) { + FONTSIGNATURE fs = {{0}}; struct gdi_font *child; - struct gdi_font_family *family; - struct gdi_font_face *child_face, *best_face = NULL; - UINT penalty = 0, new_penalty = 0; - BOOL bold, italic, bd, it; + struct gdi_font_face *face; - italic = !!font->lf.lfItalic; - bold = font->lf.lfWeight > FW_MEDIUM; + if (!(face = find_matching_face_by_name( family_name, NULL, &font->lf, fs, FALSE ))) return; - if (!(family = find_family_from_name( family_name ))) return; - - LIST_FOR_EACH_ENTRY( child_face, get_family_face_list(family), struct gdi_font_face, entry ) - { - it = !!(child_face->ntmFlags & NTM_ITALIC); - bd = !!(child_face->ntmFlags & NTM_BOLD); - new_penalty = ( it ^ italic ) + ( bd ^ bold ); - if (!best_face || new_penalty < penalty) - { - penalty = new_penalty; - best_face = child_face; - } - } - if (!best_face) return; - - if (!(child = create_gdi_font( best_face, family_name, &font->lf ))) return; + if (!(child = create_gdi_font( face, family_name, &font->lf ))) return; child->matrix = font->matrix; child->can_use_bitmap = font->can_use_bitmap; child->scale_y = font->scale_y; diff --git a/dlls/gdi32/freetype.c b/dlls/gdi32/freetype.c index 83fe76115ef..ababde731d7 100644 --- a/dlls/gdi32/freetype.c +++ b/dlls/gdi32/freetype.c @@ -557,12 +557,6 @@ static BOOL is_subpixel_rendering_enabled( void ) } -static const struct list *get_face_list_from_family(const Family *family) -{ - if (family->replacement) return &family->replacement->faces; - return &family->faces; -} - static LPWSTR strdupW(LPCWSTR p) { LPWSTR ret; @@ -2077,7 +2071,7 @@ done: } #ifdef SONAME_LIBFONTCONFIG -static Family* get_fontconfig_family(DWORD pitch_and_family, const CHARSETINFO *csi, BOOL want_vertical) +static struct gdi_font_face *get_fontconfig_face( const LOGFONTW *lf, FONTSIGNATURE fs, BOOL want_vertical ) { const char *name; WCHAR nameW[LF_FACESIZE]; @@ -2086,14 +2080,14 @@ static Family* get_fontconfig_family(DWORD pitch_and_family, const CHARSETINFO * FcResult result; FcBool r; int ret, i; - Family *family = NULL; + struct gdi_font_face *face = NULL; - if (!csi->fs.fsCsb[0]) return NULL; + if (!fs.fsCsb[0]) return NULL; - if((pitch_and_family & FIXED_PITCH) || - (pitch_and_family & 0xF0) == FF_MODERN) + if((lf->lfPitchAndFamily & FIXED_PITCH) || + (lf->lfPitchAndFamily & 0xF0) == FF_MODERN) name = "monospace"; - else if((pitch_and_family & 0xF0) == FF_ROMAN) + else if((lf->lfPitchAndFamily & 0xF0) == FF_ROMAN) name = "serif"; else name = "sans-serif"; @@ -2113,14 +2107,8 @@ static Family* get_fontconfig_family(DWORD pitch_and_family, const CHARSETINFO * best = pFcFontMatch(NULL, pat, &result); if (!best || result != FcResultMatch) goto end; - for (i = 0; - !family && pFcPatternGetString(best, FC_FAMILY, i, &str) == FcResultMatch; - i++) + for (i = 0; pFcPatternGetString(best, FC_FAMILY, i, &str) == FcResultMatch; i++) { - Face *face; - const struct gdi_font_link *font_link; - const struct list *face_list; - if (!want_vertical) { ret = MultiByteToWideChar(CP_UTF8, 0, (const char*)str, -1, @@ -2133,31 +2121,14 @@ static Family* get_fontconfig_family(DWORD pitch_and_family, const CHARSETINFO * nameW + 1, ARRAY_SIZE(nameW) - 1); } if (!ret) continue; - family = find_family_from_any_name(nameW); - if (!family) continue; - - font_link = find_gdi_font_link( family->family_name ); - face_list = get_face_list_from_family(family); - LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { - if (!face->scalable) - continue; - if (csi->fs.fsCsb[0] & face->fs.fsCsb[0]) - goto found; - if (font_link != NULL && - csi->fs.fsCsb[0] & font_link->fs.fsCsb[0]) - goto found; - } - family = NULL; + if ((face = find_matching_face_by_name( nameW, NULL, lf, fs, FALSE ))) break; } - -found: - if (family) - TRACE("got %s\n", wine_dbgstr_w(nameW)); + if (face) TRACE("got %s\n", wine_dbgstr_w(nameW)); end: pFcPatternDestroy(pat); pFcPatternDestroy(best); - return family; + return face; } #endif @@ -2261,18 +2232,14 @@ static BOOL CDECL freetype_load_font( struct gdi_font *font ) static struct gdi_font * CDECL freetype_SelectFont( DC *dc, HFONT hfont ) { struct gdi_font *font; - Face *face, *best, *best_bitmap; - Family *family, *last_resort_family; - const struct list *face_list; + Face *face; + Family *family; INT height; - unsigned int score = 0, new_score; - signed int diff = 0, newdiff; - BOOL bd, it, can_use_bitmap, want_vertical; + BOOL can_use_bitmap, want_vertical; LOGFONTW lf; CHARSETINFO csi; FMAT2 dcmat; const WCHAR *orig_name = NULL; - const struct gdi_font_link *font_link; GetObjectW( hfont, sizeof(lf), &lf ); lf.lfWidth = abs(lf.lfWidth); @@ -2328,9 +2295,6 @@ static struct gdi_font * CDECL freetype_SelectFont( DC *dc, HFONT hfont ) if(!strcmpiW(lf.lfFaceName, SymbolW)) lf.lfCharSet = SYMBOL_CHARSET; - it = !!lf.lfItalic; - bd = lf.lfWeight > 550; - if(!TranslateCharsetInfo((DWORD*)(INT_PTR)lf.lfCharSet, &csi, TCI_SRCCHARSET)) { switch(lf.lfCharSet) { case DEFAULT_CHARSET: @@ -2356,53 +2320,8 @@ static struct gdi_font * CDECL freetype_SelectFont( DC *dc, HFONT hfont ) orig_name = FaceName; } - /* We want a match on name and charset or just name if - charset was DEFAULT_CHARSET. If the latter then - we fixup the returned charset later in get_nearest_charset - where we'll either use the charset of the current ansi codepage - or if that's unavailable the first charset that the font supports. - */ - LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { - if (!strncmpiW( family->family_name, FaceName, LF_FACESIZE - 1 ) || - (subst && !strncmpiW( family->family_name, subst, LF_FACESIZE - 1 ))) - { - font_link = find_gdi_font_link( family->family_name ); - face_list = get_face_list_from_family(family); - LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { - if (!(face->scalable || can_use_bitmap)) - continue; - if (csi.fs.fsCsb[0] & face->fs.fsCsb[0]) - goto found; - if (font_link != NULL && - csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]) - goto found; - if (!csi.fs.fsCsb[0]) - goto found; - } - } - } - - /* Search by full face name. */ - LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { - face_list = get_face_list_from_family(family); - LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { - if (!strncmpiW( face->full_name, FaceName, LF_FACESIZE - 1 ) && (face->scalable || can_use_bitmap)) - { - if (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || !csi.fs.fsCsb[0]) - goto found_face; - font_link = find_gdi_font_link( family->family_name ); - if (font_link != NULL && - csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]) - goto found_face; - } - } - } - - /* - * Try check the SystemLink list first for a replacement font. - * We may find good replacements there. - */ - if ((family = find_family_from_font_links( FaceName, subst, csi.fs ))) goto found; + if ((face = find_matching_face_by_name( FaceName, subst, &lf, csi.fs, can_use_bitmap ))) + goto found_face; } orig_name = NULL; /* substitution is no longer relevant */ @@ -2433,119 +2352,25 @@ static struct gdi_font * CDECL freetype_SelectFont( DC *dc, HFONT hfont ) strcpyW(lf.lfFaceName, default_sans); else strcpyW(lf.lfFaceName, default_sans); - LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { - if (!strncmpiW( family->family_name, lf.lfFaceName, LF_FACESIZE - 1 )) - { - font_link = find_gdi_font_link( family->family_name ); - face_list = get_face_list_from_family(family); - LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { - if (!(face->scalable || can_use_bitmap)) - continue; - if (csi.fs.fsCsb[0] & face->fs.fsCsb[0]) - goto found; - if (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]) - goto found; - } - } - } + + if ((face = find_matching_face_by_name( lf.lfFaceName, NULL, &lf, csi.fs, can_use_bitmap ))) + goto found_face; #ifdef SONAME_LIBFONTCONFIG /* Try FontConfig substitutions if the face isn't found */ - family = get_fontconfig_family(lf.lfPitchAndFamily, &csi, want_vertical); - if (family) goto found; + if ((face = get_fontconfig_face( &lf, csi.fs, want_vertical ))) goto found_face; #endif - last_resort_family = NULL; - LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { - font_link = find_gdi_font_link( family->family_name ); - face_list = get_face_list_from_family(family); - LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { - if(!(face->flags & ADDFONT_VERTICAL_FONT) == !want_vertical && - (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || - (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]))) { - if(face->scalable) - goto found; - if(can_use_bitmap && !last_resort_family) - last_resort_family = family; - } - } - } - - if(last_resort_family) { - family = last_resort_family; - csi.fs.fsCsb[0] = 0; - goto found; - } - - LIST_FOR_EACH_ENTRY( family, &font_list, Family, entry ) { - face_list = get_face_list_from_family(family); - LIST_FOR_EACH_ENTRY( face, face_list, Face, entry ) { - if(face->scalable && !(face->flags & ADDFONT_VERTICAL_FONT) == !want_vertical) { - csi.fs.fsCsb[0] = 0; - WARN("just using first face for now\n"); - goto found; - } - if(can_use_bitmap && !last_resort_family) - last_resort_family = family; - } - } - if(!last_resort_family) { - FIXME("can't find a single appropriate font - bailing\n"); - return NULL; - } - - WARN("could only find a bitmap font - this will probably look awful!\n"); - family = last_resort_family; + if ((face = find_any_face( &lf, csi.fs, can_use_bitmap, want_vertical ))) goto found_face; csi.fs.fsCsb[0] = 0; - -found: - - height = lf.lfHeight; - - face = best = best_bitmap = NULL; - font_link = find_gdi_font_link( family->family_name ); - face_list = get_face_list_from_family(family); - LIST_FOR_EACH_ENTRY(face, face_list, Face, entry) - { - if (csi.fs.fsCsb[0] & face->fs.fsCsb[0] || - (font_link != NULL && csi.fs.fsCsb[0] & font_link->fs.fsCsb[0]) || - !csi.fs.fsCsb[0]) - { - BOOL italic, bold; - - italic = (face->ntmFlags & NTM_ITALIC) ? 1 : 0; - bold = (face->ntmFlags & NTM_BOLD) ? 1 : 0; - new_score = (italic ^ it) + (bold ^ bd); - if(!best || new_score <= score) - { - TRACE("(it=%d, bd=%d) is selected for (it=%d, bd=%d)\n", - italic, bold, it, bd); - score = new_score; - best = face; - if(best->scalable && score == 0) break; - if(!best->scalable) - { - if(height > 0) - newdiff = height - (signed int)(best->size.height); - else - newdiff = -height - ((signed int)(best->size.height) - best->size.internal_leading); - if(!best_bitmap || new_score < score || - (diff > 0 && newdiff < diff && newdiff >= 0) || (diff < 0 && newdiff > diff)) - { - TRACE("%d is better for %d diff was %d\n", best->size.height, height, diff); - diff = newdiff; - best_bitmap = best; - if(score == 0 && diff == 0) break; - } - } - } - } - } - if(best) - face = best->scalable ? best : best_bitmap; + if ((face = find_any_face( &lf, csi.fs, can_use_bitmap, want_vertical ))) goto found_face; + if (want_vertical && (face = find_any_face( &lf, csi.fs, can_use_bitmap, FALSE ))) goto found_face; + FIXME("can't find a single appropriate font - bailing\n"); + return NULL; found_face: height = lf.lfHeight; + family = face->family; TRACE("not in cache\n"); font = create_gdi_font( face, orig_name, &lf ); @@ -2566,9 +2391,14 @@ found_face: if(!face->scalable) { /* Windows uses integer scaling factors for bitmap fonts */ - INT scale, scaled_height; + INT scale, scaled_height, diff; struct gdi_font *cachedfont; + if (height > 0) + diff = height - (signed int)face->size.height; + else + diff = -height - ((signed int)face->size.height - face->size.internal_leading); + /* FIXME: rotation of bitmap fonts is ignored */ height = abs(GDI_ROUND( (double)height * font->matrix.eM22 )); if (font->aveWidth) diff --git a/dlls/gdi32/gdi_private.h b/dlls/gdi32/gdi_private.h index 7147d8ebc41..0345db44b7e 100644 --- a/dlls/gdi32/gdi_private.h +++ b/dlls/gdi32/gdi_private.h @@ -450,8 +450,6 @@ struct font_backend_funcs extern const WCHAR *get_gdi_font_subst( const WCHAR *from_name, int from_charset, int *to_charset ) DECLSPEC_HIDDEN; -extern struct list font_list DECLSPEC_HIDDEN; -extern struct gdi_font_family *find_family_from_any_name( const WCHAR *name ) DECLSPEC_HIDDEN; extern int add_gdi_face( const WCHAR *family_name, const WCHAR *second_name, const WCHAR *style, const WCHAR *fullname, const WCHAR *file, void *data_ptr, SIZE_T data_size, UINT index, FONTSIGNATURE fs, @@ -459,9 +457,11 @@ extern int add_gdi_face( const WCHAR *family_name, const WCHAR *second_name, const struct bitmap_font_size *size ) DECLSPEC_HIDDEN; extern struct gdi_font_link *find_gdi_font_link( const WCHAR *name ) DECLSPEC_HIDDEN; -extern struct gdi_font_family *find_family_from_font_links( const WCHAR *name, const WCHAR *subst, - FONTSIGNATURE fs ) DECLSPEC_HIDDEN; extern void create_child_font_list( struct gdi_font *font ) DECLSPEC_HIDDEN; +extern struct gdi_font_face *find_matching_face_by_name( const WCHAR *name, const WCHAR *subst, const LOGFONTW *lf, + FONTSIGNATURE fs, BOOL can_use_bitmap ) DECLSPEC_HIDDEN; +extern struct gdi_font_face *find_any_face( const LOGFONTW *lf, FONTSIGNATURE fs, + BOOL can_use_bitmap, BOOL want_vertical ) DECLSPEC_HIDDEN; extern void free_gdi_font( struct gdi_font *font ) DECLSPEC_HIDDEN; extern void cache_gdi_font( struct gdi_font *font ) DECLSPEC_HIDDEN;