From: Nikolay Sivov Subject: [PATCH 1/4] dwrite: Add simulated bold faces for each family when appropriate Message-Id: <55E7CE17.30605@codeweavers.com> Date: Thu, 03 Sep 2015 07:35:35 +0300 --- From c3ee1779824ffddcafb3260d03a301b97595e981 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Wed, 2 Sep 2015 20:50:03 +0300 Subject: [PATCH 1/4] dwrite: Add simulated bold face for each family when appropriate --- dlls/dwrite/font.c | 231 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 183 insertions(+), 48 deletions(-) diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index 928222d..215546d 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -55,7 +55,10 @@ static const WCHAR expandedW[] = {'e','x','p','a','n','d','e','d',0}; static const WCHAR italicW[] = {'i','t','a','l','i','c',0}; static const WCHAR boldW[] = {'B','o','l','d',0}; static const WCHAR obliqueW[] = {'O','b','l','i','q','u','e',0}; +static const WCHAR regularW[] = {'R','e','g','u','l','a','r',0}; +static const WCHAR demiW[] = {'d','e','m','i',0}; static const WCHAR spaceW[] = {' ',0}; +static const WCHAR enusW[] = {'e','n','-','u','s',0}; struct dwrite_font_propvec { FLOAT stretch; @@ -83,6 +86,8 @@ struct dwrite_font_data { UINT32 face_index; WCHAR *facename; + + BOOL bold_sim_tested : 1; /* used to mark font as tested when scanning for bold simulation candidate */ }; struct dwrite_fontfamily_data { @@ -1233,7 +1238,6 @@ static BOOL WINAPI dwritefont_IsSymbolFont(IDWriteFont2 *iface) static HRESULT WINAPI dwritefont_GetFaceNames(IDWriteFont2 *iface, IDWriteLocalizedStrings **names) { static const WCHAR boldobliqueW[] = {'B','o','l','d',' ','O','b','l','i','q','u','e',0}; - static const WCHAR enusW[] = {'e','n','-','u','s',0}; struct dwrite_font *This = impl_from_IDWriteFont2(iface); IDWriteLocalizedStrings *strings; @@ -1949,7 +1953,6 @@ HRESULT get_filestream_from_file(IDWriteFontFile *file, IDWriteFontFileStream ** static void fontstrings_get_en_string(IDWriteLocalizedStrings *strings, WCHAR *buffer, UINT32 size) { - static const WCHAR enusW[] = {'e','n','-','U','S',0}; BOOL exists = FALSE; UINT32 index; HRESULT hr; @@ -2016,7 +2019,7 @@ static BOOL match_pattern_list(struct list *tokens, const struct name_pattern *p continue; if (!strncmpiW(token->ptr, pattern->part1, len_part1)) { - *match = *token; + if (match) *match = *token; list_remove(&token->entry); heap_free(token); return TRUE; @@ -2039,7 +2042,7 @@ static BOOL match_pattern_list(struct list *tokens, const struct name_pattern *p continue; /* combined string match */ - *match = *token; + if (match) *match = *token; list_remove(&token->entry); heap_free(token); return TRUE; @@ -2062,8 +2065,10 @@ static BOOL match_pattern_list(struct list *tokens, const struct name_pattern *p continue; /* both parts matched, remove tokens */ - match->ptr = next_token->ptr; - match->len = (token->ptr - next_token->ptr) + token->len; + if (match) { + match->ptr = next_token->ptr; + match->len = (token->ptr - next_token->ptr) + token->len; + } list_remove(&token->entry); list_remove(&next_token->entry); heap_free(next_token); @@ -2074,8 +2079,10 @@ static BOOL match_pattern_list(struct list *tokens, const struct name_pattern *p } } - match->ptr = NULL; - match->len = 0; + if (match) { + match->ptr = NULL; + match->len = 0; + } return FALSE; } @@ -2220,7 +2227,6 @@ static DWRITE_FONT_WEIGHT font_extract_weight(struct list *tokens, DWRITE_FONT_W struct name_token *match) { static const WCHAR heavyW[] = {'h','e','a','v','y',0}; - static const WCHAR demiW[] = {'d','e','m','i',0}; static const WCHAR nordW[] = {'n','o','r','d',0}; static const struct name_pattern thin_patterns[] = { @@ -2407,7 +2413,8 @@ static inline void font_name_token_to_str(const struct name_token *name, WCHAR * strW[name->len] = 0; } -static BOOL font_apply_differentiation_rules(struct dwrite_font_data *font, WCHAR *familyW, WCHAR *faceW) +/* Modifies facenameW string, and returns pointer to regular term that was removed */ +static const WCHAR *facename_remove_regular_term(WCHAR *facenameW, INT len) { static const WCHAR bookW[] = {'B','o','o','k',0}; static const WCHAR normalW[] = {'N','o','r','m','a','l',0}; @@ -2424,20 +2431,11 @@ static BOOL font_apply_differentiation_rules(struct dwrite_font_data *font, WCHA NULL }; - struct name_token stretch_name, weight_name, style_name; - WCHAR familynameW[255], facenameW[255], finalW[255]; - WCHAR weightW[32], stretchW[32], styleW[32]; - const WCHAR *ptr, *regular_ptr = NULL; - struct name_token *token, *token2; - DWRITE_FONT_STRETCH stretch; - DWRITE_FONT_WEIGHT weight; - struct list tokens; - int len, i = 0; - WCHAR *ptrW; + const WCHAR *regular_ptr = NULL, *ptr; + int i = 0; - /* remove leading and trailing spaces from family and face name */ - trim_spaces(familyW, familynameW); - len = trim_spaces(faceW, facenameW); + if (len == -1) + len = strlenW(facenameW); /* remove rightmost regular variant from face name */ while (!regular_ptr && (ptr = regular_patterns[i++])) { @@ -2460,15 +2458,15 @@ static BOOL font_apply_differentiation_rules(struct dwrite_font_data *font, WCHA } } - /* append face name to family name, FIXME check if face name is a substring of family name */ - if (*facenameW) { - strcatW(familynameW, spaceW); - strcatW(familynameW, facenameW); - } + return regular_ptr; +} - /* tokenize with " .-_" */ - list_init(&tokens); - ptr = familynameW; +static void fontname_tokenize(struct list *tokens, const WCHAR *nameW) +{ + const WCHAR *ptr; + + list_init(tokens); + ptr = nameW; while (*ptr) { struct name_token *token = heap_alloc(sizeof(*token)); @@ -2488,9 +2486,55 @@ static BOOL font_apply_differentiation_rules(struct dwrite_font_data *font, WCHA ptr++; } - list_add_head(&tokens, &token->entry); + list_add_head(tokens, &token->entry); + } +} + +static void fontname_tokens_to_str(struct list *tokens, WCHAR *nameW) +{ + struct name_token *token, *token2; + LIST_FOR_EACH_ENTRY_SAFE_REV(token, token2, tokens, struct name_token, entry) { + int len; + + list_remove(&token->entry); + + /* don't include last separator */ + len = list_empty(tokens) ? token->len : token->fulllen; + memcpy(nameW, token->ptr, len * sizeof(WCHAR)); + nameW += len; + + heap_free(token); + } + *nameW = 0; +} + +static BOOL font_apply_differentiation_rules(struct dwrite_font_data *font, WCHAR *familyW, WCHAR *faceW) +{ + struct name_token stretch_name, weight_name, style_name; + WCHAR familynameW[255], facenameW[255], finalW[255]; + WCHAR weightW[32], stretchW[32], styleW[32]; + const WCHAR *regular_ptr = NULL; + DWRITE_FONT_STRETCH stretch; + DWRITE_FONT_WEIGHT weight; + struct list tokens; + int len; + + /* remove leading and trailing spaces from family and face name */ + trim_spaces(familyW, familynameW); + len = trim_spaces(faceW, facenameW); + + /* remove rightmost regular variant from face name */ + regular_ptr = facename_remove_regular_term(facenameW, len); + + /* append face name to family name, FIXME check if face name is a substring of family name */ + if (*facenameW) { + strcatW(familynameW, spaceW); + strcatW(familynameW, facenameW); } + /* tokenize with " .-_" */ + fontname_tokenize(&tokens, familynameW); + /* extract and resolve style */ font->style = font_extract_style(&tokens, font->style, &style_name); @@ -2530,20 +2574,7 @@ static BOOL font_apply_differentiation_rules(struct dwrite_font_data *font, WCHA /* FIXME: cleanup face name from possible 2-3 digit prefixes */ /* get final combined string from what's left in token list, list is released */ - ptrW = finalW; - LIST_FOR_EACH_ENTRY_SAFE_REV(token, token2, &tokens, struct name_token, entry) { - int len; - - list_remove(&token->entry); - - /* don't include last separator */ - len = list_empty(&tokens) ? token->len : token->fulllen; - memcpy(ptrW, token->ptr, len * sizeof(WCHAR)); - ptrW += len; - - heap_free(token); - } - *ptrW = 0; + fontname_tokens_to_str(&tokens, finalW); if (!strcmpW(familyW, finalW)) return FALSE; @@ -2656,6 +2687,7 @@ static HRESULT init_font_data(IDWriteFactory2 *factory, IDWriteFontFile *file, D data->file = file; data->face_index = face_index; data->face_type = face_type; + data->bold_sim_tested = FALSE; IDWriteFontFile_AddRef(file); IDWriteFactory2_AddRef(factory); @@ -2690,6 +2722,33 @@ static HRESULT init_font_data(IDWriteFactory2 *factory, IDWriteFontFile *file, D return S_OK; } +static HRESULT init_font_data_from_font(const struct dwrite_font_data *src, DWRITE_FONT_WEIGHT weight, const WCHAR *facenameW, + struct dwrite_font_data **ret) +{ + struct dwrite_font_data *data; + + *ret = NULL; + data = heap_alloc_zero(sizeof(*data)); + if (!data) + return E_OUTOFMEMORY; + + *data = *src; + data->ref = 1; + data->weight = weight; + memset(data->info_strings, 0, sizeof(data->info_strings)); + data->names = NULL; + IDWriteFactory2_AddRef(data->factory); + IDWriteFontFile_AddRef(data->file); + + create_localizedstrings(&data->names); + add_localizedstring(data->names, enusW, facenameW); + + init_font_prop_vec(data->weight, data->stretch, data->style, &data->propvec); + + *ret = data; + return S_OK; +}; + static HRESULT init_fontfamily_data(IDWriteLocalizedStrings *familyname, struct dwrite_fontfamily_data **ret) { struct dwrite_fontfamily_data *data; @@ -2715,11 +2774,83 @@ static HRESULT init_fontfamily_data(IDWriteLocalizedStrings *familyname, struct return S_OK; } +static void fontfamily_add_bold_simulated_face(struct dwrite_fontfamily_data *family) +{ + UINT32 i, j, heaviest; + + for (i = 0; i < family->font_count; i++) { + DWRITE_FONT_WEIGHT weight = family->fonts[i]->weight; + heaviest = i; + + if (family->fonts[i]->bold_sim_tested) + continue; + + family->fonts[i]->bold_sim_tested = TRUE; + for (j = i; j < family->font_count; j++) { + if (family->fonts[j]->bold_sim_tested) + continue; + + if ((family->fonts[i]->style == family->fonts[j]->style) && + (family->fonts[i]->stretch == family->fonts[j]->stretch)) { + if (family->fonts[j]->weight > weight) { + weight = family->fonts[j]->weight; + heaviest = j; + } + family->fonts[j]->bold_sim_tested = TRUE; + } + } + + if (weight >= DWRITE_FONT_WEIGHT_SEMI_LIGHT && weight <= 550) { + static const struct name_pattern weightsim_patterns[] = { + { extraW, lightW }, + { extW, lightW }, + { ultraW, lightW }, + { semiW, lightW }, + { semiW, boldW }, + { demiW, boldW }, + { boldW }, + { thinW }, + { lightW }, + { mediumW }, + { demiW }, + { NULL } + }; + + WCHAR facenameW[255], initialW[255]; + struct dwrite_font_data *boldface; + struct list tokens; + + /* add Bold simulation based on heaviest face data */ + + /* Simulated face name should only contain Bold as weight term, + so remove existing regular and weight terms. */ + fontstrings_get_en_string(family->fonts[heaviest]->names, initialW, sizeof(initialW)/sizeof(WCHAR)); + facename_remove_regular_term(initialW, -1); + + /* remove current weight pattern */ + fontname_tokenize(&tokens, initialW); + match_pattern_list(&tokens, weightsim_patterns, NULL); + fontname_tokens_to_str(&tokens, facenameW); + + /* Bold suffix for new name */ + if (*facenameW) + strcatW(facenameW, spaceW); + strcatW(facenameW, boldW); + + if (init_font_data_from_font(family->fonts[heaviest], DWRITE_FONT_WEIGHT_BOLD, facenameW, &boldface) == S_OK) { + boldface->bold_sim_tested = TRUE; + fontfamily_add_font(family, boldface); + } + } + } +} + HRESULT create_font_collection(IDWriteFactory2* factory, IDWriteFontFileEnumerator *enumerator, BOOL is_system, IDWriteFontCollection **ret) { struct dwrite_fontcollection *collection; BOOL current = FALSE; HRESULT hr = S_OK; + UINT32 i; *ret = NULL; @@ -2740,7 +2871,7 @@ HRESULT create_font_collection(IDWriteFactory2* factory, IDWriteFontFileEnumerat DWRITE_FONT_FACE_TYPE face_type; DWRITE_FONT_FILE_TYPE file_type; IDWriteFontFile *file; - UINT32 face_count, i; + UINT32 face_count; BOOL supported; current = FALSE; @@ -2800,7 +2931,11 @@ HRESULT create_font_collection(IDWriteFactory2* factory, IDWriteFontFileEnumerat } IDWriteFontFile_Release(file); - }; + } + + for (i = 0; i < collection->family_count; i++) { + fontfamily_add_bold_simulated_face(collection->family_data[i]); + } return hr; } -- 2.1.4