static void PangoSubstitute(FcPattern *pattern,void *context) { const char *option; option=(const char *) context; if (option == (const char *) NULL) return; FcPatternDel(pattern,FC_HINTING); FcPatternAddBool(pattern, FC_HINTING,LocaleCompare(option,"none") != 0); FcPatternDel(pattern,FC_AUTOHINT); FcPatternAddBool(pattern,FC_AUTOHINT,LocaleCompare(option,"auto") == 0); }
static void substitute_func (FcPattern *pattern, gpointer data) { if (opt_hinting != HINT_DEFAULT) { FcPatternDel (pattern, FC_HINTING); FcPatternAddBool (pattern, FC_HINTING, opt_hinting != HINT_NONE); FcPatternDel (pattern, FC_AUTOHINT); FcPatternAddBool (pattern, FC_AUTOHINT, opt_hinting == HINT_AUTO); } }
static XftFont* open_pattern(FcPattern* pattern, Antialiasing antialiasing, Hinting hinting) { #ifdef FC_HINT_STYLE static const int hintstyles[] = { FC_HINT_NONE, FC_HINT_SLIGHT, FC_HINT_MEDIUM, FC_HINT_FULL }; #endif /* FC_HINT_STYLE */ FcPattern* res_pattern; FcResult result; XftFont* font; Display* xdisplay = gdk_x11_get_default_xdisplay(); int screen = gdk_x11_get_default_screen(); res_pattern = XftFontMatch(xdisplay, screen, pattern, &result); if (res_pattern == NULL) { return NULL; } FcPatternDel(res_pattern, FC_HINTING); FcPatternAddBool(res_pattern, FC_HINTING, hinting != HINT_NONE); #ifdef FC_HINT_STYLE FcPatternDel(res_pattern, FC_HINT_STYLE); FcPatternAddInteger(res_pattern, FC_HINT_STYLE, hintstyles[hinting]); #endif /* FC_HINT_STYLE */ FcPatternDel(res_pattern, FC_ANTIALIAS); FcPatternAddBool(res_pattern, FC_ANTIALIAS, antialiasing != ANTIALIAS_NONE); FcPatternDel(res_pattern, FC_RGBA); FcPatternAddInteger(res_pattern, FC_RGBA, antialiasing == ANTIALIAS_RGBA ? FC_RGBA_RGB : FC_RGBA_NONE); FcPatternDel(res_pattern, FC_DPI); FcPatternAddInteger(res_pattern, FC_DPI, 96); font = XftFontOpenPattern(xdisplay, res_pattern); if (!font) { FcPatternDestroy(res_pattern); } return font; }
CAMLprim value pattern_del(value pat, value prop) { CAMLparam0(); FcBool res = FcPatternDel(FcPattern_val(pat), String_val(prop)); CAMLreturn(CamlFcBool(res)); }
static void default_substitute (FcPattern *pattern, gpointer data G_GNUC_UNUSED) { FcPatternDel (pattern, FC_DPI); FcPatternAddInteger (pattern, FC_DPI, opt_dpi); if (opt_hinting != HINT_DEFAULT) { FcPatternDel (pattern, FC_HINTING); FcPatternAddBool (pattern, FC_HINTING, opt_hinting != HINT_NONE); FcPatternDel (pattern, FC_AUTOHINT); FcPatternAddBool (pattern, FC_AUTOHINT, opt_hinting == HINT_AUTO); } }
FcFontSet *fcinfo_styles_index(const FcChar8 *family, const FcPattern *filter, family_info_t *fi) { FcFontSet *fontset, *result; FcPattern *pattern; pattern = FcPatternDuplicate(filter); FcPatternDel(pattern, FC_FAMILY); /* perhaps shouldn't be needed */ FcPatternAddString(pattern, FC_FAMILY, family); fontset = fcinfo(NULL, pattern, FcFalse, 5, FC_FAMILY, FC_STYLE, FC_LANG,/* fonts like Misc Fixed need to be sorted by LANG (subset-like) */ FC_PIXEL_SIZE, FC_FILE); /* for identifying the best font in FC_LANG terms */ FcPatternDestroy(pattern); if (fontset->nfont == 0) return fontset; result = fcinfo_match(fontset, filter); /* assuming family_info entries all same for each style of family; might not be true always */ if (fi) family_info(result->fonts[0], fi); FcFontSetDestroy(fontset); return result; }
// also handle an xlfd with %d for size? static char* makeFontOfSize(char *font, int size, char *fallback) { FcPattern *pattern; char *result; if (font[0]=='-') { char *fname; fname = fixXLFD(font, size); pattern = XftXlfdParse(fname, False, False); wfree(fname); } else { pattern = FcNameParse(font); } //FcPatternPrint(pattern); if (size > 0) { FcPatternDel(pattern, "pixelsize"); FcPatternAddDouble(pattern, "pixelsize", (double)size); } else if (size==0 && !hasProperty(pattern, "size") && !hasProperty(pattern, "pixelsize")) { FcPatternAddDouble(pattern, "pixelsize", (double)DEFAULT_SIZE); } if (fallback && !hasPropertyWithStringValue(pattern, "family", fallback)) { FcPatternAddString(pattern, "family", fallback); } result = FcNameUnparse(pattern); FcPatternDestroy(pattern); return result; }
FcPattern* BC_Resources::find_similar_font(FT_ULong char_code, FcPattern *oldfont) { FcPattern *pat, *font; FcFontSet *fs; FcObjectSet *os; FcCharSet *fcs; FcChar8 *file; double dval; int ival; // Do not search control codes if(char_code < ' ') return 0; fontconfig_lock.lock("BC_Resources::find_similar_font"); pat = FcPatternCreate(); os = FcObjectSetBuild(FC_FILE, FC_CHARSET, FC_SCALABLE, FC_FAMILY, FC_SLANT, FC_WEIGHT, FC_WIDTH, (char *)0); FcPatternAddBool(pat, FC_SCALABLE, true); fcs = FcCharSetCreate(); if(FcCharSetAddChar(fcs, char_code)) FcPatternAddCharSet(pat, FC_CHARSET, fcs); FcCharSetDestroy(fcs); for(int i = 0; i < LEN_FCPROP; i++) { if(FcPatternGetInteger(oldfont, fc_properties[i], 0, &ival) == FcResultMatch) FcPatternAddInteger(pat, fc_properties[i], ival); } fs = FcFontList(0, pat, os); for(int i = LEN_FCPROP - 1; i >= 0 && fs->nfont == 0; i--) { FcFontSetDestroy(fs); FcPatternDel(pat, fc_properties[i]); fs = FcFontList(0, pat, os); } FcPatternDestroy(pat); FcObjectSetDestroy(os); pat = 0; for (int i = 0; i < fs->nfont; i++) { font = fs->fonts[i]; if(FcPatternGetCharSet(font, FC_CHARSET, 0, &fcs) == FcResultMatch) { if(FcCharSetHasChar(fcs, char_code)) { pat = FcPatternDuplicate(font); break; } } } FcFontSetDestroy(fs); fontconfig_lock.unlock(); return pat; }
static void _vte_gl_fcpattern_disable_rgba(FcPattern *pattern, gpointer data) { int rgba; if (FcPatternGetInteger(pattern, FC_RGBA, 0, &rgba) != FcResultNoMatch) { FcPatternDel(pattern, FC_RGBA); } FcPatternAddInteger(pattern, FC_RGBA, FC_RGBA_NONE); }
static cairo_scaled_font_t * create_scaled_font (cairo_t * cr) { FcPattern *pattern, *resolved; FcResult result; cairo_font_face_t *font_face; cairo_scaled_font_t *scaled_font; cairo_font_options_t *font_options; cairo_matrix_t font_matrix, ctm; double pixel_size; font_options = cairo_font_options_create (); cairo_get_font_options (cr, font_options); pattern = FcPatternCreate (); FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *)"Bitstream Vera Sans"); FcPatternAddDouble (pattern, FC_PIXEL_SIZE, TEXT_SIZE); FcConfigSubstitute (NULL, pattern, FcMatchPattern); cairo_ft_font_options_substitute (font_options, pattern); FcDefaultSubstitute (pattern); resolved = FcFontMatch (NULL, pattern, &result); /* set layout to vertical */ FcPatternDel (resolved, FC_VERTICAL_LAYOUT); FcPatternAddBool (resolved, FC_VERTICAL_LAYOUT, FcTrue); FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size); font_face = cairo_ft_font_face_create_for_pattern (resolved); cairo_matrix_init_translate (&font_matrix, 10, 30); cairo_matrix_rotate (&font_matrix, M_PI_2/3); cairo_matrix_scale (&font_matrix, pixel_size, pixel_size); cairo_get_matrix (cr, &ctm); scaled_font = cairo_scaled_font_create (font_face, &font_matrix, &ctm, font_options); cairo_font_options_destroy (font_options); cairo_font_face_destroy (font_face); FcPatternDestroy (pattern); FcPatternDestroy (resolved); return scaled_font; }
static void xftfont_fix_match (FcPattern *pat, FcPattern *match) { /* These values are not used for matching (except antialias), but for rendering, so make sure they are carried over to the match. We also put antialias here because most fonts are antialiased, so the match will have antialias true. */ FcBool b = FcTrue; int i; double dpi; FcPatternGetBool (pat, FC_ANTIALIAS, 0, &b); if (! b) { FcPatternDel (match, FC_ANTIALIAS); FcPatternAddBool (match, FC_ANTIALIAS, FcFalse); } FcPatternGetBool (pat, FC_HINTING, 0, &b); if (! b) { FcPatternDel (match, FC_HINTING); FcPatternAddBool (match, FC_HINTING, FcFalse); } #ifndef FC_HINT_STYLE # define FC_HINT_STYLE "hintstyle" #endif if (FcResultMatch == FcPatternGetInteger (pat, FC_HINT_STYLE, 0, &i)) { FcPatternDel (match, FC_HINT_STYLE); FcPatternAddInteger (match, FC_HINT_STYLE, i); } #ifndef FC_LCD_FILTER /* Older fontconfig versions don't have FC_LCD_FILTER. */ #define FC_LCD_FILTER "lcdfilter" #endif if (FcResultMatch == FcPatternGetInteger (pat, FC_LCD_FILTER, 0, &i)) { FcPatternDel (match, FC_LCD_FILTER); FcPatternAddInteger (match, FC_LCD_FILTER, i); } if (FcResultMatch == FcPatternGetInteger (pat, FC_RGBA, 0, &i)) { FcPatternDel (match, FC_RGBA); FcPatternAddInteger (match, FC_RGBA, i); } if (FcResultMatch == FcPatternGetDouble (pat, FC_DPI, 0, &dpi)) { FcPatternDel (match, FC_DPI); FcPatternAddDouble (match, FC_DPI, dpi); } }
WMFont* WMCopyFontWithStyle(WMScreen *scrPtr, WMFont *font, WMFontStyle style) { FcPattern *pattern; WMFont *copy; char *name; if (!font) return NULL; pattern = FcNameParse(WMGetFontName(font)); switch (style) { case WFSNormal: FcPatternDel(pattern, "weight"); FcPatternDel(pattern, "slant"); FcPatternAddString(pattern, "weight", "regular"); FcPatternAddString(pattern, "weight", "medium"); FcPatternAddString(pattern, "slant", "roman"); break; case WFSBold: FcPatternDel(pattern, "weight"); FcPatternAddString(pattern, "weight", "bold"); break; case WFSEmphasized: FcPatternDel(pattern, "slant"); FcPatternAddString(pattern, "slant", "italic"); FcPatternAddString(pattern, "slant", "oblique"); break; case WFSBoldEmphasized: FcPatternDel(pattern, "weight"); FcPatternDel(pattern, "slant"); FcPatternAddString(pattern, "weight", "bold"); FcPatternAddString(pattern, "slant", "italic"); FcPatternAddString(pattern, "slant", "oblique"); break; } name = FcNameUnparse(pattern); copy = WMCreateFont(scrPtr, name); FcPatternDestroy(pattern); wfree(name); return copy; }
nsresult gfxQtPlatform::ResolveFontName(const nsAString& aFontName, FontResolverCallback aCallback, void *aClosure, PRBool& aAborted) { nsAutoString name(aFontName); ToLowerCase(name); nsRefPtr<FontFamily> ff; if (gPlatformFonts->Get(name, &ff) || gPlatformFontAliases->Get(name, &ff)) { aAborted = !(*aCallback)(ff->Name(), aClosure); return NS_OK; } nsCAutoString utf8Name = NS_ConvertUTF16toUTF8(aFontName); FcPattern *npat = FcPatternCreate(); FcPatternAddString(npat, FC_FAMILY, (FcChar8*)utf8Name.get()); FcObjectSet *nos = FcObjectSetBuild(FC_FAMILY, NULL); FcFontSet *nfs = FcFontList(NULL, npat, nos); for (int k = 0; k < nfs->nfont; k++) { FcChar8 *str; if (FcPatternGetString(nfs->fonts[k], FC_FAMILY, 0, (FcChar8 **) &str) != FcResultMatch) continue; nsAutoString altName = NS_ConvertUTF8toUTF16(nsDependentCString(reinterpret_cast<char*>(str))); ToLowerCase(altName); if (gPlatformFonts->Get(altName, &ff)) { printf("Adding alias: %s -> %s\n", utf8Name.get(), str); gPlatformFontAliases->Put(name, ff); aAborted = !(*aCallback)(NS_ConvertUTF8toUTF16(nsDependentCString(reinterpret_cast<char*>(str))), aClosure); goto DONE; } } FcPatternDestroy(npat); FcObjectSetDestroy(nos); FcFontSetDestroy(nfs); { npat = FcPatternCreate(); FcPatternAddString(npat, FC_FAMILY, (FcChar8*)utf8Name.get()); FcPatternDel(npat, FC_LANG); FcConfigSubstitute(NULL, npat, FcMatchPattern); FcDefaultSubstitute(npat); nos = FcObjectSetBuild(FC_FAMILY, NULL); nfs = FcFontList(NULL, npat, nos); FcResult fresult; FcPattern *match = FcFontMatch(NULL, npat, &fresult); if (match) FcFontSetAdd(nfs, match); for (int k = 0; k < nfs->nfont; k++) { FcChar8 *str; if (FcPatternGetString(nfs->fonts[k], FC_FAMILY, 0, (FcChar8 **) &str) != FcResultMatch) continue; nsAutoString altName = NS_ConvertUTF8toUTF16(nsDependentCString(reinterpret_cast<char*>(str))); ToLowerCase(altName); if (gPlatformFonts->Get(altName, &ff)) { printf("Adding alias: %s -> %s\n", utf8Name.get(), str); gPlatformFontAliases->Put(name, ff); aAborted = !(*aCallback)(NS_ConvertUTF8toUTF16(nsDependentCString(reinterpret_cast<char*>(str))), aClosure); goto DONE; } } } DONE: FcPatternDestroy(npat); FcObjectSetDestroy(nos); FcFontSetDestroy(nfs); return NS_OK; }
/** * \brief Low-level font selection. * \param priv private data * \param family font family * \param treat_family_as_pattern treat family as fontconfig pattern * \param bold font weight value * \param italic font slant value * \param index out: font index inside a file * \param code: the character that should be present in the font, can be 0 * \return font file path */ static char *select_font(ASS_Library *library, FCInstance *priv, const char *family, int treat_family_as_pattern, unsigned bold, unsigned italic, int *index, uint32_t code) { FcBool rc; FcResult result; FcPattern *pat = NULL, *rpat = NULL; int r_index, r_slant, r_weight; FcChar8 *r_family, *r_style, *r_file, *r_fullname; FcBool r_outline, r_embolden; FcCharSet *r_charset; FcFontSet *ffullname = NULL, *fsorted = NULL, *fset = NULL; int curf; char *retval = NULL; int family_cnt = 0; *index = 0; if (treat_family_as_pattern) pat = FcNameParse((const FcChar8 *) family); else pat = FcPatternCreate(); if (!pat) goto error; if (!treat_family_as_pattern) { FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) family); // In SSA/ASS fonts are sometimes referenced by their "full name", // which is usually a concatenation of family name and font // style (ex. Ottawa Bold). Full name is available from // FontConfig pattern element FC_FULLNAME, but it is never // used for font matching. // Therefore, I'm removing words from the end of the name one // by one, and adding shortened names to the pattern. It seems // that the first value (full name in this case) has // precedence in matching. // An alternative approach could be to reimplement FcFontSort // using FC_FULLNAME instead of FC_FAMILY. family_cnt = 1; { char *s = strdup(family); char *p = s + strlen(s); while (--p > s) if (*p == ' ' || *p == '-') { *p = '\0'; FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) s); ++family_cnt; } free(s); } } FcPatternAddBool(pat, FC_OUTLINE, FcTrue); FcPatternAddInteger(pat, FC_SLANT, italic); FcPatternAddInteger(pat, FC_WEIGHT, bold); FcDefaultSubstitute(pat); rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern); if (!rc) goto error; /* Fontconfig defaults include a language setting, which it sets based on * some environment variables or defaults to "en". Unset this as we don't * know the real language, and because some some attached fonts lack * non-ascii characters included in fontconfig's list of characters * required for English support and therefore don't match the lang=en * criterion. */ FcPatternDel(pat, "lang"); fsorted = FcFontSort(priv->config, pat, FcTrue, NULL, &result); ffullname = match_fullname(library, priv, family, bold, italic); if (!fsorted || !ffullname) goto error; fset = FcFontSetCreate(); for (curf = 0; curf < ffullname->nfont; ++curf) { FcPattern *curp = ffullname->fonts[curf]; FcPatternReference(curp); FcFontSetAdd(fset, curp); } for (curf = 0; curf < fsorted->nfont; ++curf) { FcPattern *curp = fsorted->fonts[curf]; FcPatternReference(curp); FcFontSetAdd(fset, curp); } for (curf = 0; curf < fset->nfont; ++curf) { FcPattern *curp = fset->fonts[curf]; result = FcPatternGetBool(curp, FC_OUTLINE, 0, &r_outline); if (result != FcResultMatch) continue; if (r_outline != FcTrue) continue; if (!code) break; result = FcPatternGetCharSet(curp, FC_CHARSET, 0, &r_charset); if (result != FcResultMatch) continue; if (FcCharSetHasChar(r_charset, code)) break; } if (curf >= fset->nfont) goto error; if (!treat_family_as_pattern) { // Remove all extra family names from original pattern. // After this, FcFontRenderPrepare will select the most relevant family // name in case there are more than one of them. for (; family_cnt > 1; --family_cnt) FcPatternRemove(pat, FC_FAMILY, family_cnt - 1); } rpat = FcFontRenderPrepare(priv->config, pat, fset->fonts[curf]); if (!rpat) goto error; result = FcPatternGetInteger(rpat, FC_INDEX, 0, &r_index); if (result != FcResultMatch) goto error; *index = r_index; result = FcPatternGetString(rpat, FC_FILE, 0, &r_file); if (result != FcResultMatch) goto error; retval = strdup((const char *) r_file); result = FcPatternGetString(rpat, FC_FAMILY, 0, &r_family); if (result != FcResultMatch) r_family = NULL; result = FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname); if (result != FcResultMatch) r_fullname = NULL; if (!treat_family_as_pattern && !(r_family && strcasecmp((const char *) r_family, family) == 0) && !(r_fullname && strcasecmp((const char *) r_fullname, family) == 0)) { char *fallback = (char *) (r_fullname ? r_fullname : r_family); if (code) { ass_msg(library, MSGL_WARN, "fontconfig: cannot find glyph U+%04X in font '%s', falling back to '%s'", (unsigned int)code, family, fallback); } else { ass_msg(library, MSGL_WARN, "fontconfig: cannot find font '%s', falling back to '%s'", family, fallback); } } result = FcPatternGetString(rpat, FC_STYLE, 0, &r_style); if (result != FcResultMatch) r_style = NULL; result = FcPatternGetInteger(rpat, FC_SLANT, 0, &r_slant); if (result != FcResultMatch) r_slant = 0; result = FcPatternGetInteger(rpat, FC_WEIGHT, 0, &r_weight); if (result != FcResultMatch) r_weight = 0; result = FcPatternGetBool(rpat, FC_EMBOLDEN, 0, &r_embolden); if (result != FcResultMatch) r_embolden = 0; ass_msg(library, MSGL_V, "Font info: family '%s', style '%s', fullname '%s'," " slant %d, weight %d%s", (const char *) r_family, (const char *) r_style, (const char *) r_fullname, r_slant, r_weight, r_embolden ? ", embolden" : ""); error: if (pat) FcPatternDestroy(pat); if (rpat) FcPatternDestroy(rpat); if (fsorted) FcFontSetDestroy(fsorted); if (ffullname) FcFontSetDestroy(ffullname); if (fset) FcFontSetDestroy(fset); return retval; }
FcFontSet * fcinfo_match(FcFontSet *fontset, const FcPattern *filter) { int f; FcFontSet *result; FcChar8 *family, *style, *file; FcChar8 *next_family, *next_style; double size; FcBool scalable; FcPattern *pat, *match = NULL; FcResult r; FcInit(); if (fontset->nfont == 0) { result = FcFontSetCreate(); return result; } /* fc-match "Misc Fixed:style=Bold" gives many files with various pixelsizes plus two "encodings" ('general' and 'ISO8859-1', ISO8859-1 is subset of general) we want only one card for family,style pair for MiscFixedBold.html we will take informations from file 9x18B.pcf.gz (biggest size and general encoding) from fcinfo(), fontset is sorted a way that pattern created from 9x18B.pcf.gz comes first in "Misc Fixed:style=Bold" interval (the biggest pixel size and subset of supported languages, see pattern_compare()). */ result = FcFontSetCreate(); for (f = 0; f < fontset->nfont; ) { /* bitmap fonts: */ /* we are here always on the best file in FC_LANG terms */ /* we need it for displaying the widest range of supported languages */ /* identify it by FC_FILE for FcFontMatch() */ if (filter) pat = FcPatternDuplicate(filter); else pat = FcPatternCreate(); assert(fcinfo_get_translated_string(fontset->fonts[f], FC_FAMILY, LANG_EN, &family) == FcResultMatch); FcPatternAddString(pat, FC_FAMILY, family); if (fcinfo_get_translated_string(fontset->fonts[f], FC_STYLE, LANG_EN, &style) == FcResultMatch) FcPatternAddString(pat, FC_STYLE, style); if (FcPatternGetString(fontset->fonts[f], FC_FILE, 0, &file) == FcResultMatch) FcPatternAddString(pat, FC_FILE, file); FcConfigSubstitute(NULL, pat, FcMatchPattern); FcDefaultSubstitute(pat); match = FcFontMatch(NULL, pat, &r); assert(r == FcResultMatch); /* should not be needed: FcConfigSubstitute(NULL, match, FcMatchFont); */ FcPatternDestroy(pat); assert(FcPatternGetBool(match, FC_SCALABLE, 0, &scalable) == FcResultMatch); /* fprintf(stdout, "Family: %s Style: %s\n", next_family, next_style); fprintf(stdout, " %s\n", file);*/ /* figure out sizes font provide (needed for specimen) and skip same */ /* family,style pairs (see e. g. Misc Fixed) */ if (! scalable) { next_family = family; next_style = style; /* unfortunately e. g. 'ETL Fixed' and 'ETL fixed' are present on my system */ while (FcStrCmpIgnoreCase(next_family, family) == 0 && FcStrCmpIgnoreCase(next_style, style) == 0) { if (FcPatternGetDouble(fontset->fonts[f], FC_PIXEL_SIZE, 0, &size) == FcResultMatch) { if (! pattern_contains_size(match, size)) { FcPatternAddDouble(match, FC_PIXEL_SIZE, size); if (FcPatternGetString(fontset->fonts[f], FC_FILE, 0, &file) == FcResultMatch) FcPatternAddString(match, FC_FILE, file); /*fprintf(stdout, " %s\n", file);*/ } } if (f + 1 == fontset->nfont) /* last family,style pair at all */ { f++; break; } assert(fcinfo_get_translated_string(fontset->fonts[f + 1], FC_FAMILY, LANG_EN, &next_family) == FcResultMatch); assert(fcinfo_get_translated_string(fontset->fonts[f + 1], FC_STYLE, LANG_EN, &next_style) == FcResultMatch); f++; } } else /* family Efont Serif Regular is spread (unintentionally) over */ { /* 5 files (Efont Serif <X> should be spread over 2 files, */ /* normal and 'Alternate'), so this is needed anyway: */ FcPatternDel(match, FC_FILE); next_family = family; next_style = style; while (FcStrCmpIgnoreCase(next_family, family) == 0 && FcStrCmpIgnoreCase(next_style, style) == 0) { if (FcPatternGetString(fontset->fonts[f], FC_FILE, 0, &file) == FcResultMatch) FcPatternAddString(match, FC_FILE, file); if (f + 1 == fontset->nfont) /* last family,style pair at all */ { f++; break; } assert(fcinfo_get_translated_string(fontset->fonts[f + 1], FC_FAMILY, LANG_EN, &next_family) == FcResultMatch); assert(fcinfo_get_translated_string(fontset->fonts[f + 1], FC_STYLE, LANG_EN, &next_style) == FcResultMatch); f++; } } /* don't add ethio16f-uni.pcf, johabg16.pcf and similar with reported empty charset */ if (! empty_charset(match)) FcFontSetAdd(result, FcPatternDuplicate(match)); } if (match) FcPatternDestroy(match); return result; }
FcFontSet *fcinfo_families_index(const FcPattern *filter) { int unused_int, f, nfamilies; FcPattern *pattern, *match; FcFontSet *fontset, *tmp, *families; FcResult r; FcChar8 *family, *next_family, *normal_style, *style; FcInit(); pattern = FcPatternDuplicate(filter); fontset = fcinfo(NULL, filter, FcFalse, 5, FC_FAMILY, FC_STYLE, FC_LANG,/* fonts like Misc Fixed need to be sorted by LANG (subset-like) */ FC_PIXEL_SIZE, FC_FILE); /* for identifying the best font in FC_LANG terms */ if (fontset->nfont == 0) return fontset; tmp = fcinfo_match(fontset, pattern); FcFontSetDestroy(fontset); fontset = tmp; if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &unused_int) != FcResultMatch) FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR); if (FcPatternGetInteger(pattern, FC_SLANT, 0, &unused_int) != FcResultMatch) FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (FcPatternGetInteger(pattern, FC_WIDTH, 0, &unused_int) != FcResultMatch) FcPatternAddInteger(pattern, FC_WIDTH, FC_WIDTH_NORMAL); /* choose 'normal' styles according to pattern */ families = FcFontSetCreate(); nfamilies = 0; for (f = 0; f < fontset->nfont; ) { assert(fcinfo_get_translated_string(fontset->fonts[f], FC_FAMILY, LANG_EN, &family) == FcResultMatch); FcPatternDel(pattern, FC_FAMILY); FcPatternAddString(pattern, FC_FAMILY, family); match = FcFontSetMatch(NULL, &fontset, 1, pattern, &r); assert(fcinfo_get_translated_string(match, FC_STYLE, LANG_EN, &normal_style) == FcResultMatch); do { assert(fcinfo_get_translated_string(fontset->fonts[f], FC_STYLE, LANG_EN, &style) == FcResultMatch); if (FcStrCmpIgnoreCase(style, normal_style) == 0) FcFontSetAdd(families, FcPatternDuplicate(fontset->fonts[f])); if (++f == fontset->nfont) break; assert(fcinfo_get_translated_string(fontset->fonts[f], FC_FAMILY, LANG_EN, &next_family) == FcResultMatch); } while (FcStrCmpIgnoreCase(family, next_family) == 0); nfamilies++; } FcPatternDestroy(pattern); FcFontSetDestroy(fontset); return families; }
static void apply_xft_settings (struct x_display_info *dpyinfo, struct xsettings *settings) { #ifdef HAVE_XFT FcPattern *pat; struct xsettings oldsettings; bool changed = false; memset (&oldsettings, 0, sizeof (oldsettings)); pat = FcPatternCreate (); XftDefaultSubstitute (dpyinfo->display, XScreenNumberOfScreen (dpyinfo->screen), pat); FcPatternGetBool (pat, FC_ANTIALIAS, 0, &oldsettings.aa); FcPatternGetBool (pat, FC_HINTING, 0, &oldsettings.hinting); #ifdef FC_HINT_STYLE FcPatternGetInteger (pat, FC_HINT_STYLE, 0, &oldsettings.hintstyle); #endif FcPatternGetInteger (pat, FC_LCD_FILTER, 0, &oldsettings.lcdfilter); FcPatternGetInteger (pat, FC_RGBA, 0, &oldsettings.rgba); FcPatternGetDouble (pat, FC_DPI, 0, &oldsettings.dpi); if ((settings->seen & SEEN_AA) != 0 && oldsettings.aa != settings->aa) { FcPatternDel (pat, FC_ANTIALIAS); FcPatternAddBool (pat, FC_ANTIALIAS, settings->aa); changed = true; oldsettings.aa = settings->aa; } if ((settings->seen & SEEN_HINTING) != 0 && oldsettings.hinting != settings->hinting) { FcPatternDel (pat, FC_HINTING); FcPatternAddBool (pat, FC_HINTING, settings->hinting); changed = true; oldsettings.hinting = settings->hinting; } if ((settings->seen & SEEN_RGBA) != 0 && oldsettings.rgba != settings->rgba) { FcPatternDel (pat, FC_RGBA); FcPatternAddInteger (pat, FC_RGBA, settings->rgba); oldsettings.rgba = settings->rgba; changed = true; } /* Older fontconfig versions don't have FC_LCD_FILTER. */ if ((settings->seen & SEEN_LCDFILTER) != 0 && oldsettings.lcdfilter != settings->lcdfilter) { FcPatternDel (pat, FC_LCD_FILTER); FcPatternAddInteger (pat, FC_LCD_FILTER, settings->lcdfilter); changed = true; oldsettings.lcdfilter = settings->lcdfilter; } #ifdef FC_HINT_STYLE if ((settings->seen & SEEN_HINTSTYLE) != 0 && oldsettings.hintstyle != settings->hintstyle) { FcPatternDel (pat, FC_HINT_STYLE); FcPatternAddInteger (pat, FC_HINT_STYLE, settings->hintstyle); changed = true; oldsettings.hintstyle = settings->hintstyle; } #endif if ((settings->seen & SEEN_DPI) != 0 && oldsettings.dpi != settings->dpi && settings->dpi > 0) { FcPatternDel (pat, FC_DPI); FcPatternAddDouble (pat, FC_DPI, settings->dpi); changed = true; oldsettings.dpi = settings->dpi; /* Changing the DPI on this display affects all frames on it. Check FRAME_RES_X and FRAME_RES_Y in frame.h to see how. */ dpyinfo->resy = dpyinfo->resx = settings->dpi; } if (changed) { static char const format[] = "Antialias: %d, Hinting: %d, RGBA: %d, LCDFilter: %d, " "Hintstyle: %d, DPI: %f"; enum { d_formats = 5, d_growth = INT_BUFSIZE_BOUND (int) - sizeof "%d", lf_formats = 1, max_f_integer_digits = DBL_MAX_10_EXP + 1, f_precision = 6, lf_growth = (sizeof "-." + max_f_integer_digits + f_precision - sizeof "%f") }; char buf[sizeof format + d_formats * d_growth + lf_formats * lf_growth]; XftDefaultSet (dpyinfo->display, pat); store_config_changed_event (Qfont_render, XCAR (dpyinfo->name_list_element)); Vxft_settings = make_formatted_string (buf, format, oldsettings.aa, oldsettings.hinting, oldsettings.rgba, oldsettings.lcdfilter, oldsettings.hintstyle, oldsettings.dpi); } else FcPatternDestroy (pat); #endif /* HAVE_XFT */ }
Bool XftPatternDel (XftPattern *p, const char *object) { return FcPatternDel (p, object); }
/* This function gets called to convert a matched pattern into what * we'll use to actually load the font. We turn off hinting since we * want metrics that are independent of scale. */ static void substitute_func (FcPattern *pattern, gpointer data) { FcPatternDel (pattern, FC_HINTING); FcPatternAddBool (pattern, FC_HINTING, FALSE); }
static cairo_status_t create_scaled_font (cairo_t * cr, cairo_scaled_font_t **out) { FcPattern *pattern, *resolved; FcResult result; cairo_font_face_t *font_face; cairo_scaled_font_t *scaled_font; cairo_font_options_t *font_options; cairo_matrix_t font_matrix, ctm; cairo_status_t status; double pixel_size; font_options = cairo_font_options_create (); cairo_get_font_options (cr, font_options); pattern = FcPatternCreate (); if (pattern == NULL) return CAIRO_STATUS_NO_MEMORY; FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *)CAIRO_TEST_FONT_FAMILY " Sans"); FcPatternAddDouble (pattern, FC_PIXEL_SIZE, TEXT_SIZE); FcConfigSubstitute (NULL, pattern, FcMatchPattern); cairo_ft_font_options_substitute (font_options, pattern); FcDefaultSubstitute (pattern); resolved = FcFontMatch (NULL, pattern, &result); if (resolved == NULL) { FcPatternDestroy (pattern); return CAIRO_STATUS_NO_MEMORY; } /* set layout to vertical */ FcPatternDel (resolved, FC_VERTICAL_LAYOUT); FcPatternAddBool (resolved, FC_VERTICAL_LAYOUT, FcTrue); FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size); font_face = cairo_ft_font_face_create_for_pattern (resolved); cairo_matrix_init_translate (&font_matrix, 10, 30); cairo_matrix_rotate (&font_matrix, M_PI_2/3); cairo_matrix_scale (&font_matrix, pixel_size, pixel_size); cairo_get_matrix (cr, &ctm); scaled_font = cairo_scaled_font_create (font_face, &font_matrix, &ctm, font_options); cairo_font_options_destroy (font_options); cairo_font_face_destroy (font_face); FcPatternDestroy (pattern); FcPatternDestroy (resolved); status = cairo_scaled_font_status (scaled_font); if (status) { cairo_scaled_font_destroy (scaled_font); return status; } *out = scaled_font; return CAIRO_STATUS_SUCCESS; }
static void apply_xft_settings (struct x_display_info *dpyinfo, struct xsettings *settings) { #ifdef HAVE_XFT FcPattern *pat; struct xsettings oldsettings; bool changed = false; memset (&oldsettings, 0, sizeof (oldsettings)); pat = FcPatternCreate (); XftDefaultSubstitute (dpyinfo->display, XScreenNumberOfScreen (dpyinfo->screen), pat); FcPatternGetBool (pat, FC_ANTIALIAS, 0, &oldsettings.aa); FcPatternGetBool (pat, FC_HINTING, 0, &oldsettings.hinting); #ifdef FC_HINT_STYLE FcPatternGetInteger (pat, FC_HINT_STYLE, 0, &oldsettings.hintstyle); #endif FcPatternGetInteger (pat, FC_LCD_FILTER, 0, &oldsettings.lcdfilter); FcPatternGetInteger (pat, FC_RGBA, 0, &oldsettings.rgba); FcPatternGetDouble (pat, FC_DPI, 0, &oldsettings.dpi); if ((settings->seen & SEEN_AA) != 0 && oldsettings.aa != settings->aa) { FcPatternDel (pat, FC_ANTIALIAS); FcPatternAddBool (pat, FC_ANTIALIAS, settings->aa); changed = true; oldsettings.aa = settings->aa; } if ((settings->seen & SEEN_HINTING) != 0 && oldsettings.hinting != settings->hinting) { FcPatternDel (pat, FC_HINTING); FcPatternAddBool (pat, FC_HINTING, settings->hinting); changed = true; oldsettings.hinting = settings->hinting; } if ((settings->seen & SEEN_RGBA) != 0 && oldsettings.rgba != settings->rgba) { FcPatternDel (pat, FC_RGBA); FcPatternAddInteger (pat, FC_RGBA, settings->rgba); oldsettings.rgba = settings->rgba; changed = true; } /* Older fontconfig versions don't have FC_LCD_FILTER. */ if ((settings->seen & SEEN_LCDFILTER) != 0 && oldsettings.lcdfilter != settings->lcdfilter) { FcPatternDel (pat, FC_LCD_FILTER); FcPatternAddInteger (pat, FC_LCD_FILTER, settings->lcdfilter); changed = true; oldsettings.lcdfilter = settings->lcdfilter; } #ifdef FC_HINT_STYLE if ((settings->seen & SEEN_HINTSTYLE) != 0 && oldsettings.hintstyle != settings->hintstyle) { FcPatternDel (pat, FC_HINT_STYLE); FcPatternAddInteger (pat, FC_HINT_STYLE, settings->hintstyle); changed = true; oldsettings.hintstyle = settings->hintstyle; } #endif if ((settings->seen & SEEN_DPI) != 0 && settings->dpi > 0 /* The following conjunct avoids setting `changed' to true when old and new dpi settings do not differ "substantially". Otherwise, the dynamic-setting Elisp code may process all sorts of unrelated settings that override users' font customizations, among others. Compare: https://lists.gnu.org/r/emacs-devel/2016-05/msg00557.html https://lists.gnu.org/r/bug-gnu-emacs/2016-12/msg00820.html As soon as the dynamic-settings code has been tested and verified, this Emacs 25.2 workaround should be removed. */ && ((oldsettings.dpi >= settings->dpi && (oldsettings.dpi - settings->dpi) > 2) || ((settings->dpi > oldsettings.dpi) && (settings->dpi - oldsettings.dpi) > 2))) { FcPatternDel (pat, FC_DPI); FcPatternAddDouble (pat, FC_DPI, settings->dpi); changed = true; oldsettings.dpi = settings->dpi; /* Changing the DPI on this display affects all frames on it. Check FRAME_RES_X and FRAME_RES_Y in frame.h to see how. */ dpyinfo->resy = dpyinfo->resx = settings->dpi; } if (changed) { static char const format[] = "Antialias: %d, Hinting: %d, RGBA: %d, LCDFilter: %d, " "Hintstyle: %d, DPI: %f"; enum { d_formats = 5, d_growth = INT_BUFSIZE_BOUND (int) - sizeof "%d", lf_formats = 1, max_f_integer_digits = DBL_MAX_10_EXP + 1, f_precision = 6, lf_growth = (sizeof "-." + max_f_integer_digits + f_precision - sizeof "%f") }; char buf[sizeof format + d_formats * d_growth + lf_formats * lf_growth]; XftDefaultSet (dpyinfo->display, pat); store_config_changed_event (Qfont_render, XCAR (dpyinfo->name_list_element)); Vxft_settings = make_formatted_string (buf, format, oldsettings.aa, oldsettings.hinting, oldsettings.rgba, oldsettings.lcdfilter, oldsettings.hintstyle, oldsettings.dpi); } else FcPatternDestroy (pat); #endif /* HAVE_XFT */ }
TGlyphInfo *glyph_cache_get_info(TGlyphCache *cache, gunichar glyph) { TGlyphInfo *gfi; XftFont *xftfont; PangoFont *font; PangoGlyph pglyph; PangoRectangle ink, logical; gint ascent, descent; gint x_offset; gint nominal_width; double scale_y, scale_x; if (!cache->hash) return NULL; gfi = g_hash_table_lookup(cache->hash, GINT_TO_POINTER(glyph)); if (gfi) return gfi; font = pango_fontset_get_font(cache->font_set, glyph); pglyph = pango_fc_font_get_glyph(PANGO_FC_FONT(font), glyph); if (!pglyph) { fprintf (stderr, "Error: Unable to find glyph %d.\n", glyph); if (!pglyph) pglyph = pango_fc_font_get_glyph(PANGO_FC_FONT(font), glyph); if (!pglyph) pglyph = PANGO_GET_UNKNOWN_GLYPH (glyph); if (!pglyph) return NULL; } pango_font_get_glyph_extents(font, pglyph, &ink, &logical); ascent = PANGO_ASCENT(logical); descent = PANGO_DESCENT(logical); xftfont = pango_xft_font_get_font(font); x_offset = 0; nominal_width = cache->width; if (g_unichar_iswide(glyph)) nominal_width *= 2; scale_x = scale_y = 1.0; if (logical.width > nominal_width) { if (logical.width) scale_x = (double)nominal_width / (double)logical.width; else scale_x = 1.0; } if (logical.height != cache->height) { double scale; if (ascent) { scale_y = (double)cache->ascent / (double)ascent; } else { scale_y = 1.0; } if (descent) { scale = (double)cache->descent / (double)descent; if (scale < scale_y) scale_y = scale; } } if (scale_x >= 1.0) { scale_x = 1.0; x_offset += (nominal_width - logical.width) / 2; } if (scale_x < 1.0 || scale_y != 1.0) { FcBool scalable; FcPatternGetBool(xftfont->pattern, FC_SCALABLE, 0, &scalable); if (!scalable) { /* Bah. Need better handling of non-scalable fonts */ if (scale_x < 1.0) scale_x = 1.0; scale_y = 1.0; } else if (scale_x < 1.0 || scale_y != 1.0) { FcPattern *pattern; FcMatrix mat; pattern = FcPatternDuplicate(xftfont->pattern); FcMatrixInit (&mat); FcMatrixScale(&mat, scale_x, scale_y); FcPatternDel (pattern, FC_MATRIX); FcPatternAddMatrix(pattern, FC_MATRIX, &mat); xftfont = XftFontOpenPattern( GDK_DISPLAY_XDISPLAY(cache->display), pattern ); } ascent = ascent * scale_y; } g_object_unref(font); // gfi = calloc(sizeof(TGlyphInfo), 1); gfi = g_slice_new(TGlyphInfo); gfi->font = xftfont; gfi->x_offset = PANGO_PIXELS(x_offset); gfi->y_offset = cache->ascent + (cache->ascent - ascent); gfi->y_offset = PANGO_PIXELS(gfi->y_offset); g_hash_table_insert(cache->hash, GINT_TO_POINTER(glyph), gfi); return gfi; }
int main (int argc, char **argv) { unsigned int id = (unsigned int) -1; int brief = 0; FcFontSet *fs; FcChar8 *format = NULL; int err = 0; int i; #if HAVE_GETOPT_LONG || HAVE_GETOPT int c; setlocale (LC_ALL, ""); #if HAVE_GETOPT_LONG while ((c = getopt_long (argc, argv, "i:bf:Vh", longopts, NULL)) != -1) #else while ((c = getopt (argc, argv, "i:bf:Vh")) != -1) #endif { switch (c) { case 'i': id = (unsigned int) strtol (optarg, NULL, 0); /* strtol() To handle -1. */ break; case 'b': brief = 1; break; case 'f': format = (FcChar8 *) strdup (optarg); break; case 'V': fprintf (stderr, "fontconfig version %d.%d.%d\n", FC_MAJOR, FC_MINOR, FC_REVISION); exit (0); case 'h': usage (argv[0], 0); default: usage (argv[0], 1); } } i = optind; #else i = 1; #endif if (i == argc) usage (argv[0], 1); fs = FcFontSetCreate (); #if defined(MIKTEX_WINDOWS) _setmode(_fileno(stdout), _O_BINARY); #endif for (; i < argc; i++) { if (!FcFreeTypeQueryAll ((FcChar8*) argv[i], id, NULL, NULL, fs)) { fprintf (stderr, _("Can't query face %u of font file %s\n"), id, argv[i]); err = 1; } } for (i = 0; i < fs->nfont; i++) { FcPattern *pat = fs->fonts[i]; if (brief) { FcPatternDel (pat, FC_CHARSET); FcPatternDel (pat, FC_LANG); } if (format) { FcChar8 *s; s = FcPatternFormat (pat, format); if (s) { printf ("%s", s); FcStrFree (s); } } else { FcPatternPrint (pat); } } FcFontSetDestroy (fs); FcFini (); return err; }
int main (int argc, char **argv) { int brief = 0; FcChar8 *format = NULL; int i; FcFontSet *fs; #if HAVE_GETOPT_LONG || HAVE_GETOPT int c; setlocale (LC_ALL, ""); #if HAVE_GETOPT_LONG while ((c = getopt_long (argc, argv, "bf:Vh", longopts, NULL)) != -1) #else while ((c = getopt (argc, argv, "bf:Vh")) != -1) #endif { switch (c) { case 'b': brief = 1; break; case 'f': format = (FcChar8 *) strdup (optarg); break; case 'V': fprintf (stderr, "fontconfig version %d.%d.%d\n", FC_MAJOR, FC_MINOR, FC_REVISION); exit (0); case 'h': usage (argv[0], 0); default: usage (argv[0], 1); } } i = optind; #else i = 1; #endif if (i == argc) usage (argv[0], 1); fs = FcFontSetCreate (); for (; i < argc; i++) { const FcChar8 *file = (FcChar8*) argv[i]; if (!FcFileIsDir (file)) FcFileScan (fs, NULL, NULL, NULL, file, FcTrue); else { FcStrSet *dirs = FcStrSetCreate (); FcStrList *strlist = FcStrListCreate (dirs); do { FcDirScan (fs, dirs, NULL, NULL, file, FcTrue); } while ((file = FcStrListNext (strlist))); FcStrListDone (strlist); FcStrSetDestroy (dirs); } } for (i = 0; i < fs->nfont; i++) { FcPattern *pat = fs->fonts[i]; if (brief) { FcPatternDel (pat, FC_CHARSET); FcPatternDel (pat, FC_LANG); } if (format) { FcChar8 *s; s = FcPatternFormat (pat, format); if (s) { printf ("%s", s); FcStrFree (s); } } else { FcPatternPrint (pat); } } FcFontSetDestroy (fs); FcFini (); return i > 0 ? 0 : 1; }
static void apply_xft_settings (struct x_display_info *dpyinfo, int send_event_p, struct xsettings *settings) { #ifdef HAVE_XFT FcPattern *pat; struct xsettings oldsettings; int changed = 0; memset (&oldsettings, 0, sizeof (oldsettings)); pat = FcPatternCreate (); XftDefaultSubstitute (dpyinfo->display, XScreenNumberOfScreen (dpyinfo->screen), pat); FcPatternGetBool (pat, FC_ANTIALIAS, 0, &oldsettings.aa); FcPatternGetBool (pat, FC_HINTING, 0, &oldsettings.hinting); #ifdef FC_HINT_STYLE FcPatternGetInteger (pat, FC_HINT_STYLE, 0, &oldsettings.hintstyle); #endif FcPatternGetInteger (pat, FC_LCD_FILTER, 0, &oldsettings.lcdfilter); FcPatternGetInteger (pat, FC_RGBA, 0, &oldsettings.rgba); FcPatternGetDouble (pat, FC_DPI, 0, &oldsettings.dpi); if ((settings->seen & SEEN_AA) != 0 && oldsettings.aa != settings->aa) { FcPatternDel (pat, FC_ANTIALIAS); FcPatternAddBool (pat, FC_ANTIALIAS, settings->aa); ++changed; oldsettings.aa = settings->aa; } if ((settings->seen & SEEN_HINTING) != 0 && oldsettings.hinting != settings->hinting) { FcPatternDel (pat, FC_HINTING); FcPatternAddBool (pat, FC_HINTING, settings->hinting); ++changed; oldsettings.hinting = settings->hinting; } if ((settings->seen & SEEN_RGBA) != 0 && oldsettings.rgba != settings->rgba) { FcPatternDel (pat, FC_RGBA); FcPatternAddInteger (pat, FC_RGBA, settings->rgba); oldsettings.rgba = settings->rgba; ++changed; } /* Older fontconfig versions don't have FC_LCD_FILTER. */ if ((settings->seen & SEEN_LCDFILTER) != 0 && oldsettings.lcdfilter != settings->lcdfilter) { FcPatternDel (pat, FC_LCD_FILTER); FcPatternAddInteger (pat, FC_LCD_FILTER, settings->lcdfilter); ++changed; oldsettings.lcdfilter = settings->lcdfilter; } #ifdef FC_HINT_STYLE if ((settings->seen & SEEN_HINTSTYLE) != 0 && oldsettings.hintstyle != settings->hintstyle) { FcPatternDel (pat, FC_HINT_STYLE); FcPatternAddInteger (pat, FC_HINT_STYLE, settings->hintstyle); ++changed; oldsettings.hintstyle = settings->hintstyle; } #endif if ((settings->seen & SEEN_DPI) != 0 && oldsettings.dpi != settings->dpi && settings->dpi > 0) { Lisp_Object frame, tail; FcPatternDel (pat, FC_DPI); FcPatternAddDouble (pat, FC_DPI, settings->dpi); ++changed; oldsettings.dpi = settings->dpi; /* Change the DPI on this display and all frames on the display. */ dpyinfo->resy = dpyinfo->resx = settings->dpi; FOR_EACH_FRAME (tail, frame) if (FRAME_X_P (XFRAME (frame)) && FRAME_X_DISPLAY_INFO (XFRAME (frame)) == dpyinfo) XFRAME (frame)->resy = XFRAME (frame)->resx = settings->dpi; }