FcPattern *fcinfo_get_font(const FcChar8 *request) { FcPattern *pattern, *match; FcResult r; FcChar8 *string; int integer; double double_num; pattern = fcinfo_name_parse((FcChar8 *) request); if (FcPatternGetString(pattern, FC_FAMILY, 0, &string) != FcResultMatch) FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *)"sans-serif"); if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &double_num) != FcResultMatch) FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12.0); if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &integer) != FcResultMatch) FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR); if (FcPatternGetInteger(pattern, FC_SLANT, 0, &integer) != FcResultMatch) FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ROMAN); if (FcPatternGetInteger(pattern, FC_WIDTH, 0, &integer) != FcResultMatch) FcPatternAddInteger(pattern, FC_WIDTH, FC_WIDTH_NORMAL); FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); match = FcFontMatch(0, pattern, &r); assert(r == FcResultMatch); FcPatternDestroy(pattern); return match; }
// ----------------------------------------- font_manager_match_description --- char * font_manager_match_description( font_manager_t * self, const char * family, const float size, const int bold, const int italic ) { // Use of fontconfig is disabled by default. #if 1 return 0; #else # if defined _WIN32 || defined _WIN64 fprintf( stderr, "\"font_manager_match_description\" not implemented for windows.\n" ); return 0; # endif char *filename = 0; int weight = FC_WEIGHT_REGULAR; int slant = FC_SLANT_ROMAN; if ( bold ) { weight = FC_WEIGHT_BOLD; } if( italic ) { slant = FC_SLANT_ITALIC; } FcInit(); FcPattern *pattern = FcPatternCreate(); FcPatternAddDouble( pattern, FC_SIZE, size ); FcPatternAddInteger( pattern, FC_WEIGHT, weight ); FcPatternAddInteger( pattern, FC_SLANT, slant ); FcPatternAddString( pattern, FC_FAMILY, (FcChar8*) family ); FcConfigSubstitute( 0, pattern, FcMatchPattern ); FcDefaultSubstitute( pattern ); FcResult result; FcPattern *match = FcFontMatch( 0, pattern, &result ); FcPatternDestroy( pattern ); if ( !match ) { fprintf( stderr, "fontconfig error: could not match family '%s'", family ); return 0; } else { FcValue value; FcResult result = FcPatternGet( match, FC_FILE, 0, &value ); if ( result ) { fprintf( stderr, "fontconfig error: could not match family '%s'", family ); } else { filename = strdup( (char *)(value.u.s) ); } } FcPatternDestroy( match ); return filename; #endif }
FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) { // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm) // says that we must find an exact match for font family, slant (italic or oblique can be used) // and font weight (we only match bold/non-bold here). RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); String familyNameString(getFamilyNameStringFromFontDescriptionAndFamily(fontDescription, family)); if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data()))) return 0; bool italic = fontDescription.italic(); if (!FcPatternAddInteger(pattern.get(), FC_SLANT, italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN)) return 0; if (!FcPatternAddInteger(pattern.get(), FC_WEIGHT, fontWeightToFontconfigWeight(fontDescription.weight()))) return 0; if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize())) return 0; // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp): // Allow Fontconfig to do pre-match substitution. Unless we are accessing a "fallback" // family like "sans," this is the only time we allow Fontconfig to substitute one // family name for another (i.e. if the fonts are aliased to each other). FcConfigSubstitute(0, pattern.get(), FcMatchPattern); FcDefaultSubstitute(pattern.get()); FcChar8* fontConfigFamilyNameAfterConfiguration; FcPatternGetString(pattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterConfiguration); String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration)); FcResult fontConfigResult; RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(0, pattern.get(), &fontConfigResult)); if (!resultPattern) // No match. return 0; FcChar8* fontConfigFamilyNameAfterMatching; FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching); String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching)); // If Fontconfig gave use a different font family than the one we requested, we should ignore it // and allow WebCore to give us the next font on the CSS fallback list. The only exception is if // this family name is a commonly used generic family. if (!equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching) && !(equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif") || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace") || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive"))) return 0; // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman. // If this font doesn't have one of these three encodings, don't select it. FontPlatformData* platformData = new FontPlatformData(resultPattern.get(), fontDescription); if (!platformData->hasCompatibleCharmap()) { delete platformData; return 0; } return platformData; }
FontFileLister::CollectionResult FontConfigFontFileLister::GetFontPaths(std::string const& facename, int bold, bool italic, std::set<wxUniChar> const& characters) { CollectionResult ret; std::string family(facename); if (family[0] == '@') family.erase(0, 1); boost::to_lower(family); int weight = bold == 0 ? 80 : bold == 1 ? 200 : bold; int slant = italic ? 110 : 0; // Create a fontconfig pattern to match the desired weight/slant agi::scoped_holder<FcPattern*> pat(FcPatternCreate(), FcPatternDestroy); if (!pat) return ret; FcPatternAddBool(pat, FC_OUTLINE, true); FcPatternAddInteger(pat, FC_SLANT, slant); FcPatternAddInteger(pat, FC_WEIGHT, weight); FcDefaultSubstitute(pat); if (!FcConfigSubstitute(config, pat, FcMatchPattern)) return ret; // Create a font set with only correctly named fonts // This is needed because the patterns returned by font matching only // include the first family and fullname, so we can't always verify that // we got the actual font we were asking for after the fact agi::scoped_holder<FcFontSet*> fset(FcFontSetCreate(), FcFontSetDestroy); find_font(FcConfigGetFonts(config, FcSetApplication), fset, family); find_font(FcConfigGetFonts(config, FcSetSystem), fset, family); // Get the best match from fontconfig FcResult result; FcFontSet *sets[] = { (FcFontSet*)fset }; agi::scoped_holder<FcPattern*> match(FcFontSetMatch(config, sets, 1, pat, &result), FcPatternDestroy); if (!match) return ret; FcChar8 *file; if(FcPatternGetString(match, FC_FILE, 0, &file) != FcResultMatch) return ret; FcCharSet *charset; if (FcPatternGetCharSet(match, FC_CHARSET, 0, &charset) == FcResultMatch) { for (wxUniChar chr : characters) { if (!FcCharSetHasChar(charset, chr)) ret.missing += chr; } } ret.paths.emplace_back((const char *)file); return ret; }
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); } }
// ------------------------------------------------------ match_description --- char * //match_description( char * description ) match_description( const char * face, bool bold=false, bool italic=false ) { #if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) fprintf( stderr, "\"font_manager_match_description\" " "not implemented for windows.\n" ); return 0; #endif char *filename = 0; FcInit(); //FcPattern *pattern = FcNameParse((const FcChar8*)description); FcPattern *pattern = FcPatternCreate(); if(!pattern) { fprintf( stderr, "fontconfig error: could not match face '%s'", face ); return 0; } FcPatternAddString(pattern, FC_FAMILY, (const FcChar8*)face); FcPatternAddInteger(pattern, FC_WEIGHT, bold?FC_WEIGHT_BOLD:FC_WEIGHT_REGULAR); FcPatternAddInteger(pattern, FC_SLANT, italic?FC_SLANT_ITALIC:FC_SLANT_ROMAN); FcConfigSubstitute( 0, pattern, FcMatchPattern ); FcDefaultSubstitute( pattern ); FcResult result; FcPattern *match = FcFontMatch( 0, pattern, &result ); FcPatternDestroy( pattern ); if ( !match ) { fprintf( stderr, "fontconfig error: could not match face '%s'", face ); return 0; } else { FcValue value; FcResult result = FcPatternGet( match, FC_FILE, 0, &value ); if ( result ) { fprintf( stderr, "fontconfig error: could not match face '%s'", face ); } else { filename = strdup( (char *)(value.u.s) ); } } FcPatternDestroy( match ); return filename; }
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; }
QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const { QStringList fallbackFamilies; FcPattern *pattern = FcPatternCreate(); if (!pattern) return fallbackFamilies; FcValue value; value.type = FcTypeString; QByteArray cs = family.toUtf8(); value.u.s = (const FcChar8 *)cs.data(); FcPatternAdd(pattern,FC_FAMILY,value,true); int slant_value = FC_SLANT_ROMAN; if (style == QFont::StyleItalic) slant_value = FC_SLANT_ITALIC; else if (style == QFont::StyleOblique) slant_value = FC_SLANT_OBLIQUE; FcPatternAddInteger(pattern, FC_SLANT, slant_value); if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') { Q_ASSERT(script < QUnicodeTables::ScriptCount); FcLangSet *ls = FcLangSetCreate(); FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]); FcPatternAddLangSet(pattern, FC_LANG, ls); FcLangSetDestroy(ls); } const char *stylehint = getFcFamilyForStyleHint(styleHint); if (stylehint) { value.u.s = (const FcChar8 *)stylehint; FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue); } FcConfigSubstitute(0, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult result = FcResultMatch; FcFontSet *fontSet = FcFontSort(0,pattern,FcFalse,0,&result); FcPatternDestroy(pattern); if (fontSet) { for (int i = 0; i < fontSet->nfont; i++) { FcChar8 *value = 0; if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch) continue; // capitalize(value); QString familyName = QString::fromUtf8((const char *)value); if (!fallbackFamilies.contains(familyName,Qt::CaseInsensitive)) { fallbackFamilies << familyName; } } FcFontSetDestroy(fontSet); } // qDebug() << "fallbackFamilies for:" << family << fallbackFamilies; return fallbackFamilies; }
gchar* find_font_with_property (FcConfig* fontconfig, const gchar* characters, const gchar* property) { FcPattern* pattern; FcCharSet* character_set; FcObjectSet* font_properties; FcFontSet* fonts; FcPattern* font; FcChar8* path; gchar* result; gchar* remaining_characters; gunichar character; if (fontconfig == NULL) { g_warning("Font config not loaded."); return NULL; } result = NULL; pattern = FcPatternCreate (); character_set = FcCharSetCreate (); remaining_characters = (gchar*) characters; while (TRUE) { character = g_utf8_get_char (remaining_characters); if (character == '\0') { break; } FcCharSetAddChar(character_set, character); remaining_characters = g_utf8_next_char (remaining_characters); } FcPatternAddCharSet (pattern, FC_CHARSET, character_set); FcCharSetDestroy (character_set); FcPatternAddInteger (pattern, FC_SLANT, FC_SLANT_ROMAN); FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); font_properties = FcObjectSetBuild (property, NULL); fonts = FcFontList (fontconfig, pattern, font_properties); if (fonts && fonts->nfont > 0) { font = fonts->fonts[0]; if (FcPatternGetString(font, property, 0, &path) == FcResultMatch) { result = g_strdup ((gchar*) path); } } if (fonts) { FcFontSetDestroy(fonts); } if (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; }
FcPattern *CreateFcPattern(Font font) { LTIMING("CreateXftFont"); int hg = abs(font.GetHeight()); if(hg == 0) hg = 10; String face = font.GetFaceName(); FcPattern *p = FcPatternCreate(); FcPatternAddString(p, FC_FAMILY, (FcChar8*)~face); FcPatternAddInteger(p, FC_SLANT, font.IsItalic() ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); FcPatternAddInteger(p, FC_PIXEL_SIZE, hg); FcPatternAddInteger(p, FC_WEIGHT, font.IsBold() ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL); FcPatternAddBool(p, FC_MINSPACE, 1); FcConfigSubstitute(0, p, FcMatchPattern); FcDefaultSubstitute(p); FcResult result; FcPattern *m = FcFontMatch(0, p, &result); FcPatternDestroy(p); return m; }
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); }
/** Find a font file from its family name. * @param font_config fontconfig instance * @param font_name name of the font * @return full path to the font file */ gchar* find_font_file (FcConfig* font_config, const gchar* font_name) { const FcChar8* name; FcPattern* search_pattern; FcPattern* font; FcChar8* file; gchar* path; FcObjectSet* font_properties; FcFontSet* fonts; int i; if (font_config == NULL) { g_warning("Font config not loaded."); return NULL; } path = NULL; name = font_name; search_pattern = FcPatternCreate (); FcPatternAddString (search_pattern, FC_FAMILY, name); FcPatternAddBool (search_pattern, FC_SCALABLE, FcTrue); FcPatternAddInteger (search_pattern, FC_WEIGHT, FC_WEIGHT_MEDIUM); FcPatternAddInteger (search_pattern, FC_SLANT, FC_SLANT_ROMAN); font_properties = FcObjectSetBuild (FC_FILE, NULL); fonts = FcFontList (font_config, search_pattern, font_properties); if (fonts->nfont > 0) { for (i = 0; i < fonts->nfont; i++) { font = fonts->fonts[i]; if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch) { path = g_strdup ((gchar*) file); break; } } FcPatternDestroy (font); } FcPatternDestroy (search_pattern); return path; }
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); } }
static void xftfont_add_rendering_parameters (FcPattern *pat, Lisp_Object entity) { Lisp_Object tail; int ival; for (tail = AREF (entity, FONT_EXTRA_INDEX); CONSP (tail); tail = XCDR (tail)) { Lisp_Object key = XCAR (XCAR (tail)); Lisp_Object val = XCDR (XCAR (tail)); if (EQ (key, QCantialias)) FcPatternAddBool (pat, FC_ANTIALIAS, NILP (val) ? FcFalse : FcTrue); else if (EQ (key, QChinting)) FcPatternAddBool (pat, FC_HINTING, NILP (val) ? FcFalse : FcTrue); else if (EQ (key, QCautohint)) FcPatternAddBool (pat, FC_AUTOHINT, NILP (val) ? FcFalse : FcTrue); else if (EQ (key, QChintstyle)) { if (INTEGERP (val)) FcPatternAddInteger (pat, FC_HINT_STYLE, XINT (val)); else if (SYMBOLP (val) && FcNameConstant (SDATA (SYMBOL_NAME (val)), &ival)) FcPatternAddInteger (pat, FC_HINT_STYLE, ival); } else if (EQ (key, QCrgba)) { if (INTEGERP (val)) FcPatternAddInteger (pat, FC_RGBA, XINT (val)); else if (SYMBOLP (val) && FcNameConstant (SDATA (SYMBOL_NAME (val)), &ival)) FcPatternAddInteger (pat, FC_RGBA, ival); } else if (EQ (key, QClcdfilter)) { if (INTEGERP (val)) FcPatternAddInteger (pat, FC_LCD_FILTER, ival = XINT (val)); else if (SYMBOLP (val) && FcNameConstant (SDATA (SYMBOL_NAME (val)), &ival)) FcPatternAddInteger (pat, FC_LCD_FILTER, ival); } #ifdef FC_EMBOLDEN else if (EQ (key, QCembolden)) FcPatternAddBool (pat, FC_EMBOLDEN, NILP (val) ? FcFalse : FcTrue); #endif } }
QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const { QStringList fallbackFamilies; FcPattern *pattern = FcPatternCreate(); if (!pattern) return fallbackFamilies; FcValue value; value.type = FcTypeString; QByteArray cs = family.toUtf8(); value.u.s = (const FcChar8 *)cs.data(); FcPatternAdd(pattern,FC_FAMILY,value,true); int slant_value = FC_SLANT_ROMAN; if (style == QFont::StyleItalic) slant_value = FC_SLANT_ITALIC; else if (style == QFont::StyleOblique) slant_value = FC_SLANT_OBLIQUE; FcPatternAddInteger(pattern, FC_SLANT, slant_value); Q_ASSERT(uint(script) < QChar::ScriptCount); if (*specialLanguages[script] != '\0') { FcLangSet *ls = FcLangSetCreate(); FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]); FcPatternAddLangSet(pattern, FC_LANG, ls); FcLangSetDestroy(ls); } else if (!family.isEmpty()) { // If script is Common or Han, then it may include languages like CJK, // we should attach system default language set to the pattern // to obtain correct font fallback list (i.e. if LANG=zh_CN // then we normally want to use a Chinese font for CJK text; // while a Japanese font should be used for that if LANG=ja) FcPattern *dummy = FcPatternCreate(); FcDefaultSubstitute(dummy); FcChar8 *lang = 0; FcResult res = FcPatternGetString(dummy, FC_LANG, 0, &lang); if (res == FcResultMatch) FcPatternAddString(pattern, FC_LANG, lang); FcPatternDestroy(dummy); } const char *stylehint = getFcFamilyForStyleHint(styleHint); if (stylehint) { value.u.s = (const FcChar8 *)stylehint; FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue); } FcConfigSubstitute(0, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult result = FcResultMatch; FcFontSet *fontSet = FcFontSort(0,pattern,FcFalse,0,&result); FcPatternDestroy(pattern); if (fontSet) { for (int i = 0; i < fontSet->nfont; i++) { FcChar8 *value = 0; if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch) continue; // capitalize(value); QString familyName = QString::fromUtf8((const char *)value); if (!fallbackFamilies.contains(familyName,Qt::CaseInsensitive) && familyName.compare(family, Qt::CaseInsensitive)) { fallbackFamilies << familyName; } } FcFontSetDestroy(fontSet); } // qDebug() << "fallbackFamilies for:" << family << style << styleHint << script << fallbackFamilies; return fallbackFamilies; }
bool SkFontConfigInterfaceDirect::matchFamilyName(const char familyName[], SkTypeface::Style style, FontIdentity* outIdentity, SkString* outFamilyName, SkTypeface::Style* outStyle) { SkString familyStr(familyName ? familyName : ""); if (familyStr.size() > kMaxFontFamilyLength) { return false; } SkAutoMutexAcquire ac(mutex_); FcPattern* pattern = FcPatternCreate(); if (familyName) { FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName); } FcPatternAddInteger(pattern, FC_WEIGHT, (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL); FcPatternAddInteger(pattern, FC_SLANT, (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); // Font matching: // CSS often specifies a fallback list of families: // font-family: a, b, c, serif; // However, fontconfig will always do its best to find *a* font when asked // for something so we need a way to tell if the match which it has found is // "good enough" for us. Otherwise, we can return NULL which gets piped up // and lets WebKit know to try the next CSS family name. However, fontconfig // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we // wish to support that. // // Thus, if a specific family is requested we set @family_requested. Then we // record two strings: the family name after config processing and the // family name after resolving. If the two are equal, it's a good match. // // So consider the case where a user has mapped Arial to Helvetica in their // config. // requested family: "Arial" // post_config_family: "Helvetica" // post_match_family: "Helvetica" // -> good match // // and for a missing font: // requested family: "Monaco" // post_config_family: "Monaco" // post_match_family: "Times New Roman" // -> BAD match // // However, we special-case fallback fonts; see IsFallbackFontAllowed(). const char* post_config_family = get_name(pattern, FC_FAMILY); if (!post_config_family) { // we can just continue with an empty name, e.g. default font post_config_family = ""; } FcResult result; FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); if (!font_set) { FcPatternDestroy(pattern); return false; } FcPattern* match = MatchFont(font_set, post_config_family, familyStr); if (!match) { FcPatternDestroy(pattern); FcFontSetDestroy(font_set); return false; } FcPatternDestroy(pattern); // From here out we just extract our results from 'match' post_config_family = get_name(match, FC_FAMILY); if (!post_config_family) { FcFontSetDestroy(font_set); return false; } const char* c_filename = get_name(match, FC_FILE); if (!c_filename) { FcFontSetDestroy(font_set); return false; } int face_index; if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { FcFontSetDestroy(font_set); return false; } FcFontSetDestroy(font_set); if (outIdentity) { outIdentity->fTTCIndex = face_index; outIdentity->fString.set(c_filename); } if (outFamilyName) { outFamilyName->set(post_config_family); } if (outStyle) { *outStyle = GetFontStyle(match); } return true; }
/** * \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; }
_X_EXPORT FcPattern * XftXlfdParse (const char *xlfd_orig, FcBool ignore_scalable, FcBool complete) { FcPattern *pat; const char *xlfd = xlfd_orig; const char *foundry; const char *family; const char *weight_name; const char *slant; const char *registry; const char *encoding; char *save; int pixel; int point; int resx; int resy; int slant_value, weight_value; double dpixel; if (*xlfd != '-') return NULL; if (!(xlfd = strchr (foundry = ++xlfd, '-'))) return NULL; if (!(xlfd = strchr (family = ++xlfd, '-'))) return NULL; if (!(xlfd = strchr (weight_name = ++xlfd, '-'))) return NULL; if (!(xlfd = strchr (slant = ++xlfd, '-'))) return NULL; if (!(xlfd = strchr (/* setwidth_name = */ ++xlfd, '-'))) return NULL; if (!(xlfd = strchr (/* add_style_name = */ ++xlfd, '-'))) return NULL; if (!(xlfd = XftGetInt (++xlfd, &pixel))) return NULL; if (!(xlfd = XftGetInt (++xlfd, &point))) return NULL; if (!(xlfd = XftGetInt (++xlfd, &resx))) return NULL; if (!(xlfd = XftGetInt (++xlfd, &resy))) return NULL; if (!(xlfd = strchr (/* spacing = */ ++xlfd, '-'))) return NULL; if (!(xlfd = strchr (/* average_width = */ ++xlfd, '-'))) return NULL; if (!(xlfd = strchr (registry = ++xlfd, '-'))) return NULL; /* make sure no fields follow this one */ if ((xlfd = strchr (encoding = ++xlfd, '-'))) return NULL; if (!pixel) return NULL; pat = FcPatternCreate (); if (!pat) return NULL; save = (char *) malloc (strlen (foundry) + 1); if (!save) { FcPatternDestroy (pat); return NULL; } if (!FcPatternAddString (pat, XFT_XLFD, (FcChar8 *) xlfd_orig)) goto bail; XftSplitStr (foundry, save); if (save[0] && strcmp (save, "*") != 0) if (!FcPatternAddString (pat, FC_FOUNDRY, (FcChar8 *) save)) goto bail; XftSplitStr (family, save); if (save[0] && strcmp (save, "*") != 0) if (!FcPatternAddString (pat, FC_FAMILY, (FcChar8 *) save)) goto bail; weight_value = _XftMatchSymbolic (XftXlfdWeights, NUM_XLFD_WEIGHTS, XftSplitStr (weight_name, save), FC_WEIGHT_MEDIUM); if (!FcPatternAddInteger (pat, FC_WEIGHT, weight_value)) goto bail; slant_value = _XftMatchSymbolic (XftXlfdSlants, NUM_XLFD_SLANTS, XftSplitStr (slant, save), FC_SLANT_ROMAN); if (!FcPatternAddInteger (pat, FC_SLANT, slant_value)) goto bail; dpixel = (double) pixel; if (point > 0) { if (!FcPatternAddDouble (pat, FC_SIZE, ((double) point) / 10.0)) goto bail; if (pixel <= 0 && resy > 0) { dpixel = (double) point * (double) resy / 720.0; } } if (dpixel > 0) if (!FcPatternAddDouble (pat, FC_PIXEL_SIZE, dpixel)) goto bail; free (save); return pat; bail: free (save); FcPatternDestroy (pat); return NULL; }
FcPattern * FcNameParse (const FcChar8 *name) { FcChar8 *save; FcPattern *pat; double d; FcChar8 *e; FcChar8 delim; FcValue v; FcMatrix m; const FcObjectType *t; const FcConstant *c; /* freed below */ save = malloc (strlen ((char *) name) + 1); if (!save) goto bail0; pat = FcPatternCreate (); if (!pat) goto bail1; for (;;) { name = FcNameFindNext (name, "-,:", save, &delim); if (save[0]) { if (!FcPatternAddString (pat, FC_FAMILY, save)) goto bail2; } if (delim != ',') break; } if (delim == '-') { for (;;) { name = FcNameFindNext (name, "-,:", save, &delim); d = strtod ((char *) save, (char **) &e); if (e != save) { if (!FcPatternAddDouble (pat, FC_SIZE, d)) goto bail2; } if (delim != ',') break; } } while (delim == ':') { name = FcNameFindNext (name, "=_:", save, &delim); if (save[0]) { if (delim == '=' || delim == '_') { t = FcNameGetObjectType ((char *) save); for (;;) { name = FcNameFindNext (name, ":,", save, &delim); if (t) { v = FcNameConvert (t->type, save, &m); if (!FcPatternAdd (pat, t->object, v, FcTrue)) { switch (v.type) { case FcTypeCharSet: FcCharSetDestroy ((FcCharSet *) v.u.c); break; case FcTypeLangSet: FcLangSetDestroy ((FcLangSet *) v.u.l); break; default: break; } goto bail2; } switch (v.type) { case FcTypeCharSet: FcCharSetDestroy ((FcCharSet *) v.u.c); break; case FcTypeLangSet: FcLangSetDestroy ((FcLangSet *) v.u.l); break; default: break; } } if (delim != ',') break; } } else { if ((c = FcNameGetConstant (save))) { if (!FcPatternAddInteger (pat, c->object, c->value)) goto bail2; } } } } free (save); return pat; bail2: FcPatternDestroy (pat); bail1: free (save); bail0: return 0; }
int face_from_options(lua_State* L) { FcChar8 * font_path, * fullname, * familyname; FcPattern* p; FcPattern* matched; FcResult result; int index = 0; const char *family = "Gentium"; double pointSize = 12; int slant = FC_SLANT_ROMAN; int weight = 100; const char *script = "latin"; const char *language = "eng"; const char *style = ""; if (!lua_istable(L, 1)) return 0; lua_pushstring(L, "size"); lua_gettable(L, -2); if (lua_isnumber(L, -1)) { pointSize = lua_tonumber(L, -1); } lua_pop(L,1); lua_pushstring(L, "filename"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { font_path = lua_tostring(L, -1); lua_pop(L,1); lua_newtable(L); lua_pushstring(L, "filename"); lua_pushstring(L, (char*)font_path); lua_settable(L, -3); goto done_match; } lua_pop(L,1); lua_pushstring(L, "font"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { family = lua_tostring(L, -1); } lua_pop(L,1); lua_pushstring(L, "weight"); lua_gettable(L, -2); if (lua_isnumber(L, -1)) { int newWeight = lua_tointeger(L, -1); #if FC_VERSION >= 21191 newWeight = FcWeightFromOpenType(newWeight); #else if (newWeight <= 100) newWeight = FC_WEIGHT_THIN; else if (newWeight <= 200) newWeight = FC_WEIGHT_ULTRALIGHT; else if (newWeight <= 300) newWeight = FC_WEIGHT_LIGHT; else if (newWeight <= 400) newWeight = FC_WEIGHT_NORMAL; else if (newWeight <= 500) newWeight = FC_WEIGHT_MEDIUM; else if (newWeight <= 600) newWeight = FC_WEIGHT_DEMIBOLD; else if (newWeight <= 700) newWeight = FC_WEIGHT_BOLD; else if (newWeight <= 800) newWeight = FC_WEIGHT_ULTRABOLD; else newWeight = FC_WEIGHT_HEAVY; #endif weight = newWeight; } lua_pop(L,1); lua_pushstring(L, "language"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { language = lua_tostring(L, -1); } lua_pop(L,1); lua_pushstring(L, "style"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { style = lua_tostring(L, -1); if (strcasestr(style,"italic")) { slant = FC_SLANT_ITALIC; style = "\0"; } else if (strcasestr(style, "oblique")) { slant = FC_SLANT_OBLIQUE; style = "\0"; } } lua_pop(L,1); p = FcPatternCreate(); FcPatternAddString (p, FC_FAMILY, (FcChar8*)(family)); FcPatternAddDouble (p, FC_SIZE, pointSize); if (strlen(style) > 0) { FcPatternAddString (p, FC_STYLE, style); } else { FcPatternAddInteger(p, FC_SLANT, slant); } FcPatternAddInteger(p, FC_WEIGHT, weight); // /* Add fallback fonts here. Some of the standard 14 should be fine. */ FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Times-Roman"); FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Times"); FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Helvetica"); FcConfigSubstitute (NULL, p, FcMatchFont); FcDefaultSubstitute (p); matched = FcFontMatch (0, p, &result); if (FcPatternGetString (matched, FC_FILE, 0, &font_path) != FcResultMatch) return 0; FcPatternGetInteger(matched, FC_INDEX, 0, &index); font_path = (FcChar8 *)strdup((char*)font_path); if (!font_path) { printf("Finding font path failed\n"); return 0; } /* Push back slant and weight, we need to pass them to libpdftex */ FcPatternGetInteger(matched, FC_SLANT, 0, &slant); FcPatternGetInteger(matched, FC_WEIGHT, 0, &weight); /* Find out which family we did actually pick up */ if (FcPatternGetString (matched, FC_FAMILY, 0, &familyname) != FcResultMatch) return 0; lua_newtable(L); lua_pushstring(L, "filename"); lua_pushstring(L, (char*)font_path); lua_settable(L, -3); lua_pushstring(L, "family"); lua_pushstring(L, (char*)(familyname)); lua_settable(L, -3); if (FcPatternGetString (matched, FC_FULLNAME, 0, &fullname) == FcResultMatch) { lua_pushstring(L, "fullname"); lua_pushstring(L, (char*)(fullname)); lua_settable(L, -3); } FcPatternDestroy (matched); FcPatternDestroy (p); done_match: lua_pushstring(L, "index"); lua_pushinteger(L, index); lua_settable(L, -3); lua_pushstring(L, "pointsize"); lua_pushnumber(L, pointSize); lua_settable(L, -3); return 1; }
/** * \brief Low-level font selection. * \param priv private data * \param family font family * \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(fc_instance_t* priv, const char* family, 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* fset = NULL; int curf; char* retval = NULL; int family_cnt; *index = 0; pat = FcPatternCreate(); if (!pat) goto error; 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; fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result); if (!fset) goto error; 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 (FC_VERSION >= 20297) // 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); #endif 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 (!(r_family && strcasecmp((const char*)r_family, family) == 0) && !(r_fullname && strcasecmp((const char*)r_fullname, family) == 0)) mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne, (const char*)(r_fullname ? r_fullname : r_family), family); 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; mp_msg(MSGT_ASS, MSGL_V, "[ass] Font info: family '%s', style '%s', fullname '%s'," " slant %d, weight %d%s\n", (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 (fset) FcFontSetDestroy(fset); return retval; }
int face_from_options(lua_State* L) { FT_Face face; FcChar8 * font_path, * fullname, * familyname; FcPattern* p; FcPattern* matched; FcResult result; int index = 0; const char *family = "Gentium"; double pointSize = 12; int slant = FC_SLANT_ROMAN; int weight = 100; const char *script = "latin"; const char *language = "eng"; int direction = HB_DIRECTION_LTR; if (!lua_istable(L, 1)) return 0; lua_pushstring(L, "size"); lua_gettable(L, -2); if (lua_isnumber(L, -1)) { pointSize = lua_tonumber(L, -1); } lua_pop(L,1); lua_pushstring(L, "filename"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { font_path = lua_tostring(L, -1); lua_pop(L,1); lua_newtable(L); lua_pushstring(L, "filename"); lua_pushstring(L, (char*)font_path); lua_settable(L, -3); goto done_match; } lua_pop(L,1); lua_pushstring(L, "font"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { family = lua_tostring(L, -1); } lua_pop(L,1); lua_pushstring(L, "weight"); lua_gettable(L, -2); if (lua_isnumber(L, -1)) { int newWeight = lua_tointeger(L, -1); if (newWeight <= 100) newWeight = FC_WEIGHT_THIN; else if (newWeight <= 200) newWeight = FC_WEIGHT_ULTRALIGHT; else if (newWeight <= 300) newWeight = FC_WEIGHT_LIGHT; else if (newWeight <= 400) newWeight = FC_WEIGHT_NORMAL; else if (newWeight <= 500) newWeight = FC_WEIGHT_MEDIUM; else if (newWeight <= 600) newWeight = FC_WEIGHT_DEMIBOLD; else if (newWeight <= 700) newWeight = FC_WEIGHT_BOLD; else if (newWeight <= 800) newWeight = FC_WEIGHT_ULTRABOLD; else newWeight = FC_WEIGHT_HEAVY; weight = newWeight; } lua_pop(L,1); lua_pushstring(L, "language"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { language = lua_tostring(L, -1); } lua_pop(L,1); lua_pushstring(L, "style"); lua_gettable(L, -2); if (lua_isstring(L, -1)) { const char* newStyleAsText = lua_tostring(L, -1); if (!strcmp(newStyleAsText, "italic")) slant = FC_SLANT_ITALIC; } lua_pop(L,1); p = FcPatternCreate(); FcPatternAddString (p, FC_FAMILY, (FcChar8*)(family)); FcPatternAddDouble (p, FC_SIZE, pointSize); FcPatternAddInteger(p, FC_SLANT, slant); FcPatternAddInteger(p, FC_WEIGHT, weight); // /* Add fallback fonts here. Some of the standard 14 should be fine. */ FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Times-Roman"); FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Times"); FcPatternAddString (p, FC_FAMILY,(FcChar8*) "Helvetica"); matched = FcFontMatch (0, p, &result); if (FcPatternGetString (matched, FC_FILE, 0, &font_path) != FcResultMatch) return 0; FcPatternGetInteger(matched, FC_INDEX, 0, &index); font_path = (FcChar8 *)strdup((char*)font_path); /* XXX signedness problems? */ if (!font_path) { printf("Finding font path failed\n"); return 0; } /* Push back slant and weight, we need to pass them to libpdftex */ FcPatternGetInteger(matched, FC_SLANT, 0, &slant); FcPatternGetInteger(matched, FC_WEIGHT, 0, &weight); /* Find out which family we did actually pick up */ if (FcPatternGetString (matched, FC_FAMILY, 0, &familyname) != FcResultMatch) return 0; lua_newtable(L); lua_pushstring(L, "filename"); lua_pushstring(L, (char*)font_path); lua_settable(L, -3); lua_pushstring(L, "family"); lua_pushstring(L, (char*)(familyname)); lua_settable(L, -3); if (FcPatternGetString (matched, FC_FULLNAME, 0, &fullname) == FcResultMatch) { lua_pushstring(L, "fullname"); lua_pushstring(L, (char*)(fullname)); lua_settable(L, -3); } FcPatternDestroy (matched); FcPatternDestroy (p); done_match: face = (FT_Face)malloc(sizeof(FT_Face)); if (FT_New_Face(ft_library, (char*)font_path, index, &face)) return 0; if (FT_Set_Char_Size(face,pointSize * 64.0, 0, 0, 0)) return 0; lua_pushstring(L, "index"); lua_pushinteger(L, index); lua_settable(L, -3); lua_pushstring(L, "pointsize"); lua_pushnumber(L, pointSize); lua_settable(L, -3); lua_pushstring(L, "face"); lua_pushlightuserdata(L, face); lua_settable(L, -3); return 1; }
bool FontConfigDirect::Match(std::string* result_family, unsigned* result_filefaceid, bool filefaceid_valid, unsigned filefaceid, const std::string& family, const void* data, size_t characters_bytes, bool* is_bold, bool* is_italic) { if (family.length() > kMaxFontFamilyLength) return false; SkAutoMutexAcquire ac(mutex_); // Given |family|, |is_bold| and |is_italic| but not |data|, the result will // be a function of these three parameters, and thus eligible for caching. // This is the fast path for |SkTypeface::CreateFromName()|. bool eligible_for_cache = !family.empty() && is_bold && is_italic && !data; if (eligible_for_cache) { int style = (*is_bold ? SkTypeface::kBold : 0 ) | (*is_italic ? SkTypeface::kItalic : 0); FontMatchKey key = FontMatchKey(family, style); const std::map<FontMatchKey, FontMatch>::const_iterator i = font_match_cache_.find(key); if (i != font_match_cache_.end()) { *is_bold = i->second.is_bold; *is_italic = i->second.is_italic; if (result_family) *result_family = i->second.family; if (result_filefaceid) *result_filefaceid = i->second.filefaceid; return true; } } FcPattern* pattern = FcPatternCreate(); if (filefaceid_valid) { const std::map<unsigned, std::string>::const_iterator i = fileid_to_filename_.find(FileFaceIdToFileId(filefaceid)); if (i == fileid_to_filename_.end()) { FcPatternDestroy(pattern); return false; } int face_index = filefaceid & 0xfu; FcPatternAddString(pattern, FC_FILE, reinterpret_cast<const FcChar8*>(i->second.c_str())); // face_index is added only when family is empty because it is not // necessary to uniquiely identify a font if both file and // family are given. if (family.empty()) FcPatternAddInteger(pattern, FC_INDEX, face_index); } if (!family.empty()) { FcPatternAddString(pattern, FC_FAMILY, (FcChar8*) family.c_str()); } FcCharSet* charset = NULL; if (data) { charset = FcCharSetCreate(); const uint16_t* chars = (const uint16_t*) data; size_t num_chars = characters_bytes / 2; for (size_t i = 0; i < num_chars; ++i) { if (U16_IS_SURROGATE(chars[i]) && U16_IS_SURROGATE_LEAD(chars[i]) && i != num_chars - 1 && U16_IS_TRAIL(chars[i + 1])) { FcCharSetAddChar(charset, U16_GET_SUPPLEMENTARY(chars[i], chars[i+1])); i++; } else { FcCharSetAddChar(charset, chars[i]); } } FcPatternAddCharSet(pattern, FC_CHARSET, charset); FcCharSetDestroy(charset); // pattern now owns it. } FcPatternAddInteger(pattern, FC_WEIGHT, is_bold && *is_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL); FcPatternAddInteger(pattern, FC_SLANT, is_italic && *is_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); // Font matching: // CSS often specifies a fallback list of families: // font-family: a, b, c, serif; // However, fontconfig will always do its best to find *a* font when asked // for something so we need a way to tell if the match which it has found is // "good enough" for us. Otherwise, we can return NULL which gets piped up // and lets WebKit know to try the next CSS family name. However, fontconfig // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we // wish to support that. // // Thus, if a specific family is requested we set @family_requested. Then we // record two strings: the family name after config processing and the // family name after resolving. If the two are equal, it's a good match. // // So consider the case where a user has mapped Arial to Helvetica in their // config. // requested family: "Arial" // post_config_family: "Helvetica" // post_match_family: "Helvetica" // -> good match // // and for a missing font: // requested family: "Monaco" // post_config_family: "Monaco" // post_match_family: "Times New Roman" // -> BAD match // // However, we special-case fallback fonts; see IsFallbackFontAllowed(). FcChar8* post_config_family; FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family); FcResult result; FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result); if (!font_set) { FcPatternDestroy(pattern); return false; } FcPattern* match = MatchFont(font_set, post_config_family, family); if (!match) { FcPatternDestroy(pattern); FcFontSetDestroy(font_set); return false; } FcPatternDestroy(pattern); FcChar8* c_filename; if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) { FcFontSetDestroy(font_set); return false; } int face_index; if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) { FcFontSetDestroy(font_set); return false; } FontMatch font_match; if (filefaceid_valid) { font_match.filefaceid = filefaceid; } else { unsigned out_fileid; const std::string filename(reinterpret_cast<char*>(c_filename)); const std::map<std::string, unsigned>::const_iterator i = filename_to_fileid_.find(filename); if (i == filename_to_fileid_.end()) { out_fileid = next_file_id_++; filename_to_fileid_[filename] = out_fileid; fileid_to_filename_[out_fileid] = filename; } else { out_fileid = i->second; } // fileid stored in filename_to_fileid_ and fileid_to_filename_ is // unique only up to the font file. We have to encode face_index for // the out param. font_match.filefaceid = FileIdAndFaceIndexToFileFaceId(out_fileid, face_index); } bool success = GetFontProperties(match, &font_match.family, &font_match.is_bold, &font_match.is_italic); FcFontSetDestroy(font_set); if (success) { // If eligible, cache the result of the matching. if (eligible_for_cache) { int style = (*is_bold ? SkTypeface::kBold : 0 ) | (*is_italic ? SkTypeface::kItalic : 0); font_match_cache_[FontMatchKey(family, style)] = font_match; } if (result_family) *result_family = font_match.family; if (result_filefaceid) *result_filefaceid = font_match.filefaceid; if (is_bold) *is_bold = font_match.is_bold; if (is_italic) *is_italic = font_match.is_italic; } return success; }
FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName) : m_pattern(0) , m_fallbacks(0) , m_size(fontDescription.computedSize()) , m_syntheticBold(false) , m_syntheticOblique(false) , m_scaledFont(0) , m_face(0) { FontPlatformData::init(); CString familyNameString = familyName.string().utf8(); const char* fcfamily = familyNameString.data(); int fcslant = FC_SLANT_ROMAN; // FIXME: Map all FontWeight values to fontconfig weights. int fcweight = FC_WEIGHT_NORMAL; float fcsize = fontDescription.computedSize(); if (fontDescription.italic()) fcslant = FC_SLANT_ITALIC; if (fontDescription.weight() >= FontWeight600) fcweight = FC_WEIGHT_BOLD; FcConfig *config = FcConfigGetCurrent(); //printf("family = %s\n", fcfamily); int type = fontDescription.genericFamily(); FcPattern* pattern = FcPatternCreate(); if (!FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fcfamily))) goto freePattern; switch (type) { case FontDescription::SerifFamily: fcfamily = "serif"; break; case FontDescription::SansSerifFamily: fcfamily = "sans-serif"; break; case FontDescription::MonospaceFamily: fcfamily = "monospace"; break; case FontDescription::NoFamily: case FontDescription::StandardFamily: default: fcfamily = "sans-serif"; } if (!FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8*>(fcfamily))) goto freePattern; if (!FcPatternAddInteger(pattern, FC_SLANT, fcslant)) goto freePattern; if (!FcPatternAddInteger(pattern, FC_WEIGHT, fcweight)) goto freePattern; if (!FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fcsize)) goto freePattern; FcConfigSubstitute(config, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); FcResult fcresult; m_pattern = FcFontMatch(config, pattern, &fcresult); FcPatternReference(m_pattern); // FIXME: should we set some default font? if (!m_pattern) goto freePattern; FcChar8 *fc_filename; char *filename; int id; id = 0; if (FcPatternGetString(m_pattern, FC_FILE, 0, &fc_filename) != FcResultMatch) { LOG(FontEngine, "cannot retrieve font\n"); goto freePattern; } filename = (char *) fc_filename; //use C cast as FcChar is a fontconfig type //printf("filename = %s\n", filename); if (FcPatternGetInteger(m_pattern, FC_INDEX, 0, &id) != FcResultMatch) { LOG(FontEngine, "cannot retrieve font index\n"); goto freePattern; } if (FT_Error error = FT_New_Face(m_library, filename, id, &m_face)) { //if (FT_Error error = FT_New_Face(m_library, too, id, &m_face)) { LOG(FontEngine, "fail to open fonti %s with index %d (error = 0x%x)\n", filename, id, error); m_face = 0; goto freePattern; } FT_Set_Pixel_Sizes(m_face, 0, static_cast<uint> (fontDescription.computedSize())); //DBGML(MODULE_FONTS, LEVEL_INFO, "open font %s with size %d\n", filename, static_cast<uint> (fontDescription.specifiedSize())); freePattern: FcPatternDestroy(pattern); FcConfigDestroy(config); }
/** * \brief Low-level font selection. * \param priv private data * \param family font family * \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(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index, uint32_t code) { FcBool rc; FcResult result; FcPattern *pat = 0, *rpat; int val_i; FcChar8* val_s; FcBool val_b; FcCharSet* val_cs; FcFontSet* fset = 0; int curf; char* retval = 0; *index = 0; pat = FcPatternCreate(); if (!pat) goto error; FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)family); 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; fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result); for (curf = 0; curf < fset->nfont; ++curf) { rpat = fset->fonts[curf]; result = FcPatternGetBool(rpat, FC_OUTLINE, 0, &val_b); if (result != FcResultMatch) continue; if (val_b != FcTrue) continue; if (!code) break; result = FcPatternGetCharSet(rpat, FC_CHARSET, 0, &val_cs); if (result != FcResultMatch) continue; if (FcCharSetHasChar(val_cs, code)) break; } if (curf >= fset->nfont) goto error; rpat = fset->fonts[curf]; result = FcPatternGetInteger(rpat, FC_INDEX, 0, &val_i); if (result != FcResultMatch) goto error; *index = val_i; result = FcPatternGetString(rpat, FC_FAMILY, 0, &val_s); if (result != FcResultMatch) goto error; if (strcasecmp((const char*)val_s, family) != 0) mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne, (const char*)val_s, family); result = FcPatternGetString(rpat, FC_FILE, 0, &val_s); if (result != FcResultMatch) goto error; retval = strdup((const char*)val_s); error: if (pat) FcPatternDestroy(pat); if (fset) FcFontSetDestroy(fset); return retval; }
std::unique_ptr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) { // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm) // says that we must find an exact match for font family, slant (italic or oblique can be used) // and font weight (we only match bold/non-bold here). RefPtr<FcPattern> pattern = adoptRef(FcPatternCreate()); // Never choose unscalable fonts, as they pixelate when displayed at different sizes. FcPatternAddBool(pattern.get(), FC_SCALABLE, FcTrue); String familyNameString(getFamilyNameStringFromFamily(family)); if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data()))) return nullptr; bool italic = fontDescription.italic(); if (!FcPatternAddInteger(pattern.get(), FC_SLANT, italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN)) return nullptr; if (!FcPatternAddInteger(pattern.get(), FC_WEIGHT, fontWeightToFontconfigWeight(fontDescription.weight()))) return nullptr; if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize())) return nullptr; // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp): // // We do not normally allow fontconfig to substitute one font family for another, since this // would break CSS font family fallback: the website should be in control of fallback. During // normal font matching, the only font family substitution permitted is for generic families // (sans, serif, monospace) or for strongly-aliased fonts (which are to be treated as // effectively identical). This is because the font matching step is designed to always find a // match for the font, which we don't want. // // Fontconfig is used in two stages: (1) configuration and (2) matching. During the // configuration step, before any matching occurs, we allow arbitrary family substitutions, // since this is an exact matter of respecting the user's font configuration. FcConfigSubstitute(nullptr, pattern.get(), FcMatchPattern); FcDefaultSubstitute(pattern.get()); FcChar8* fontConfigFamilyNameAfterConfiguration; FcPatternGetString(pattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterConfiguration); String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration)); FcResult fontConfigResult; RefPtr<FcPattern> resultPattern = adoptRef(FcFontMatch(nullptr, pattern.get(), &fontConfigResult)); if (!resultPattern) // No match. return nullptr; FcChar8* fontConfigFamilyNameAfterMatching; FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching); String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching)); // If Fontconfig gave us a different font family than the one we requested, we should ignore it // and allow WebCore to give us the next font on the CSS fallback list. The exceptions are if // this family name is a commonly-used generic family, or if the families are strongly-aliased. // Checking for a strong alias comes last, since it is slow. if (!equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching) && !(equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif") || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace") || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive")) && !areStronglyAliased(familyNameAfterConfiguration, familyNameAfterMatching)) return nullptr; // Verify that this font has an encoding compatible with Fontconfig. Fontconfig currently // supports three encodings in FcFreeTypeCharIndex: Unicode, Symbol and AppleRoman. // If this font doesn't have one of these three encodings, don't select it. auto platformData = std::make_unique<FontPlatformData>(resultPattern.get(), fontDescription); if (!platformData->hasCompatibleCharmap()) return nullptr; return platformData; }
/*** * \brief Selects a font matching family, bold, italic provided ***/ char* FontConfig_Select( filter_t *p_filter, const char* family, bool b_bold, bool b_italic, int i_size, int *i_idx ) { FcResult result = FcResultMatch; FcPattern *pat, *p_pat; FcChar8* val_s; FcBool val_b; char *ret = NULL; FcConfig* config = NULL; VLC_UNUSED(p_filter); /* Create a pattern and fills it */ pat = FcPatternCreate(); if (!pat) return NULL; /* */ FcPatternAddString( pat, FC_FAMILY, (const FcChar8*)family ); FcPatternAddBool( pat, FC_OUTLINE, FcTrue ); FcPatternAddInteger( pat, FC_SLANT, b_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN ); FcPatternAddInteger( pat, FC_WEIGHT, b_bold ? FC_WEIGHT_EXTRABOLD : FC_WEIGHT_NORMAL ); if( i_size != -1 ) { char *psz_fontsize; if( asprintf( &psz_fontsize, "%d", i_size ) != -1 ) { FcPatternAddString( pat, FC_SIZE, (const FcChar8 *)psz_fontsize ); free( psz_fontsize ); } } /* */ FcDefaultSubstitute( pat ); if( !FcConfigSubstitute( config, pat, FcMatchPattern ) ) { FcPatternDestroy( pat ); return NULL; } /* Find the best font for the pattern, destroy the pattern */ p_pat = FcFontMatch( config, pat, &result ); FcPatternDestroy( pat ); if( !p_pat || result == FcResultNoMatch ) return NULL; /* Check the new pattern */ if( ( FcResultMatch != FcPatternGetBool( p_pat, FC_OUTLINE, 0, &val_b ) ) || ( val_b != FcTrue ) ) { FcPatternDestroy( p_pat ); return NULL; } if( FcResultMatch != FcPatternGetInteger( p_pat, FC_INDEX, 0, i_idx ) ) { *i_idx = 0; } if( FcResultMatch != FcPatternGetString( p_pat, FC_FAMILY, 0, &val_s ) ) { FcPatternDestroy( p_pat ); return NULL; } /* if( strcasecmp((const char*)val_s, family ) != 0 ) msg_Warn( p_filter, "fontconfig: selected font family is not" "the requested one: '%s' != '%s'\n", (const char*)val_s, family ); */ if( FcResultMatch == FcPatternGetString( p_pat, FC_FILE, 0, &val_s ) ) ret = strdup( (const char*)val_s ); FcPatternDestroy( p_pat ); return ret; }
static Lisp_Object xftfont_open (struct frame *f, Lisp_Object entity, int pixel_size) { FcResult result; Display *display = FRAME_X_DISPLAY (f); Lisp_Object val, filename, idx, font_object; FcPattern *pat = NULL, *match; struct xftfont_info *xftfont_info = NULL; struct font *font; double size = 0; XftFont *xftfont = NULL; int spacing; char name[256]; int len, i; XGlyphInfo extents; FT_Face ft_face; FcMatrix *matrix; val = assq_no_quit (QCfont_entity, AREF (entity, FONT_EXTRA_INDEX)); if (! CONSP (val)) return Qnil; val = XCDR (val); filename = XCAR (val); idx = XCDR (val); size = XINT (AREF (entity, FONT_SIZE_INDEX)); if (size == 0) size = pixel_size; pat = FcPatternCreate (); FcPatternAddInteger (pat, FC_WEIGHT, FONT_WEIGHT_NUMERIC (entity)); i = FONT_SLANT_NUMERIC (entity) - 100; if (i < 0) i = 0; FcPatternAddInteger (pat, FC_SLANT, i); FcPatternAddInteger (pat, FC_WIDTH, FONT_WIDTH_NUMERIC (entity)); FcPatternAddDouble (pat, FC_PIXEL_SIZE, pixel_size); val = AREF (entity, FONT_FAMILY_INDEX); if (! NILP (val)) FcPatternAddString (pat, FC_FAMILY, (FcChar8 *) SDATA (SYMBOL_NAME (val))); val = AREF (entity, FONT_FOUNDRY_INDEX); if (! NILP (val)) FcPatternAddString (pat, FC_FOUNDRY, (FcChar8 *) SDATA (SYMBOL_NAME (val))); val = AREF (entity, FONT_SPACING_INDEX); if (! NILP (val)) FcPatternAddInteger (pat, FC_SPACING, XINT (val)); val = AREF (entity, FONT_DPI_INDEX); if (! NILP (val)) { double dbl = XINT (val); FcPatternAddDouble (pat, FC_DPI, dbl); } val = AREF (entity, FONT_AVGWIDTH_INDEX); if (INTEGERP (val) && XINT (val) == 0) FcPatternAddBool (pat, FC_SCALABLE, FcTrue); /* This is necessary to identify the exact font (e.g. 10x20.pcf.gz over 10x20-ISO8859-1.pcf.gz). */ FcPatternAddCharSet (pat, FC_CHARSET, ftfont_get_fc_charset (entity)); xftfont_add_rendering_parameters (pat, entity); FcPatternAddString (pat, FC_FILE, (FcChar8 *) SDATA (filename)); FcPatternAddInteger (pat, FC_INDEX, XINT (idx)); block_input (); /* Make sure that the Xrender extension is added before the Xft one. Otherwise, the close-display hook set by Xft is called after the one for Xrender, and the former tries to re-add the latter. This results in inconsistency of internal states and leads to X protocol error when one reconnects to the same X server. (Bug#1696) */ { int event_base, error_base; XRenderQueryExtension (display, &event_base, &error_base); } /* Substitute in values from X resources and XftDefaultSet. */ XftDefaultSubstitute (display, FRAME_X_SCREEN_NUMBER (f), pat); match = XftFontMatch (display, FRAME_X_SCREEN_NUMBER (f), pat, &result); xftfont_fix_match (pat, match); FcPatternDestroy (pat); xftfont = XftFontOpenPattern (display, match); if (!xftfont) { unblock_input (); XftPatternDestroy (match); return Qnil; } ft_face = XftLockFace (xftfont); unblock_input (); /* We should not destroy PAT here because it is kept in XFTFONT and destroyed automatically when XFTFONT is closed. */ font_object = font_make_object (VECSIZE (struct xftfont_info), entity, size); ASET (font_object, FONT_TYPE_INDEX, Qxft); len = font_unparse_xlfd (entity, size, name, 256); if (len > 0) ASET (font_object, FONT_NAME_INDEX, make_string (name, len)); len = font_unparse_fcname (entity, size, name, 256); if (len > 0) ASET (font_object, FONT_FULLNAME_INDEX, make_string (name, len)); else ASET (font_object, FONT_FULLNAME_INDEX, AREF (font_object, FONT_NAME_INDEX)); ASET (font_object, FONT_FILE_INDEX, filename); ASET (font_object, FONT_FORMAT_INDEX, ftfont_font_format (xftfont->pattern, filename)); font = XFONT_OBJECT (font_object); font->pixel_size = size; font->driver = &xftfont_driver; font->encoding_charset = font->repertory_charset = -1; xftfont_info = (struct xftfont_info *) font; xftfont_info->display = display; xftfont_info->xftfont = xftfont; /* This means that there's no need of transformation. */ xftfont_info->matrix.xx = 0; if (FcPatternGetMatrix (xftfont->pattern, FC_MATRIX, 0, &matrix) == FcResultMatch) { xftfont_info->matrix.xx = 0x10000L * matrix->xx; xftfont_info->matrix.yy = 0x10000L * matrix->yy; xftfont_info->matrix.xy = 0x10000L * matrix->xy; xftfont_info->matrix.yx = 0x10000L * matrix->yx; } if (INTEGERP (AREF (entity, FONT_SPACING_INDEX))) spacing = XINT (AREF (entity, FONT_SPACING_INDEX)); else spacing = FC_PROPORTIONAL; if (! ascii_printable[0]) { int ch; for (ch = 0; ch < 95; ch++) ascii_printable[ch] = ' ' + ch; } block_input (); /* Unfortunately Xft doesn't provide a way to get minimum char width. So, we set min_width to space_width. */ if (spacing != FC_PROPORTIONAL #ifdef FC_DUAL && spacing != FC_DUAL #endif /* FC_DUAL */ ) { font->min_width = font->max_width = font->average_width = font->space_width = xftfont->max_advance_width; XftTextExtents8 (display, xftfont, ascii_printable + 1, 94, &extents); } else { XftTextExtents8 (display, xftfont, ascii_printable, 1, &extents); font->min_width = font->max_width = font->space_width = extents.xOff; if (font->space_width <= 0) /* dirty workaround */ font->space_width = pixel_size; XftTextExtents8 (display, xftfont, ascii_printable + 1, 94, &extents); font->average_width = (font->space_width + extents.xOff) / 95; } unblock_input (); font->ascent = xftfont->ascent; font->descent = xftfont->descent; if (pixel_size >= 5) { /* The above condition is a dirty workaround because XftTextExtents8 behaves strangely for some fonts (e.g. "Dejavu Sans Mono") when pixel_size is less than 5. */ if (font->ascent < extents.y) font->ascent = extents.y; if (font->descent < extents.height - extents.y) font->descent = extents.height - extents.y; } font->height = font->ascent + font->descent; if (XINT (AREF (entity, FONT_SIZE_INDEX)) == 0) { int upEM = ft_face->units_per_EM; font->underline_position = -ft_face->underline_position * size / upEM; font->underline_thickness = ft_face->underline_thickness * size / upEM; if (font->underline_thickness > 2) font->underline_position -= font->underline_thickness / 2; } else { font->underline_position = -1; font->underline_thickness = 0; } #ifdef HAVE_LIBOTF xftfont_info->maybe_otf = (ft_face->face_flags & FT_FACE_FLAG_SFNT) != 0; xftfont_info->otf = NULL; #endif /* HAVE_LIBOTF */ xftfont_info->ft_size = ft_face->size; font->baseline_offset = 0; font->relative_compose = 0; font->default_ascent = 0; font->vertical_centering = 0; #ifdef FT_BDF_H if (! (ft_face->face_flags & FT_FACE_FLAG_SFNT)) { BDF_PropertyRec rec; if (FT_Get_BDF_Property (ft_face, "_MULE_BASELINE_OFFSET", &rec) == 0 && rec.type == BDF_PROPERTY_TYPE_INTEGER) font->baseline_offset = rec.u.integer; if (FT_Get_BDF_Property (ft_face, "_MULE_RELATIVE_COMPOSE", &rec) == 0 && rec.type == BDF_PROPERTY_TYPE_INTEGER) font->relative_compose = rec.u.integer; if (FT_Get_BDF_Property (ft_face, "_MULE_DEFAULT_ASCENT", &rec) == 0 && rec.type == BDF_PROPERTY_TYPE_INTEGER) font->default_ascent = rec.u.integer; } #endif return font_object; }
/** * grx_font_load_full: * @family: (nullable): the font family name or %NULL * @size: the preferred size in points or -1 for any size * @dpi: the screen resolution or -1 to ignore dpi * @weight: the font weight (e.g. bold) or -1 for any weight * @slant: the font slant (e.g. italic) or -1 for any slant * @width: the font width (e.g. narrow) or -1 for any width * @monospace: set to %TRUE to prefer a monospace font * @lang: (nullable): a RFC-3066-style language code or %NULL * @script: (nullable): an ISO 15924 script code or %NULL * @err: pointer to hold an error * * Loads the font that best matches the parameters. * * Uses fontconfig for font matching. * * Returns: (transfer full) (nullable): the font or %NULL if there was an error */ GrxFont *grx_font_load_full(const gchar *family, gint size, gint dpi, GrxFontWeight weight, GrxFontSlant slant, GrxFontWidth width, gboolean monospace, const gchar *lang, const gchar *script, GError **err) { FcPattern *pattern, *match; FcResult result; GrxFont *font; FcChar8 *file; if (!FcInit()) { g_set_error_literal(err, GRX_ERROR, GRX_ERROR_FONT_ERROR, "Failed to init fontconfig"); return NULL; } pattern = FcPatternCreate(); FcPatternAddBool(pattern, FC_SCALABLE, FcFalse); FcPatternAddBool(pattern, FC_OUTLINE, FcFalse); FcPatternAddString(pattern, FC_FONTFORMAT, (FcChar8 *)"PCF"); if (family) { FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *)family); } if (size >= 0) { FcPatternAddDouble(pattern, FC_SIZE, size); } if (dpi >= 0) { FcPatternAddDouble(pattern, FC_DPI, dpi); } switch (weight) { case GRX_FONT_WEIGHT_REGULAR: FcPatternAddInteger(pattern, FC_WEIGHT, 80); break; case GRX_FONT_WEIGHT_BOLD: FcPatternAddInteger(pattern, FC_WEIGHT, 200); break; } switch (slant) { case GRX_FONT_SLANT_REGULAR: FcPatternAddInteger(pattern, FC_SLANT, 0); break; case GRX_FONT_SLANT_ITALIC: FcPatternAddInteger(pattern, FC_SLANT, 100); break; } switch (width) { case GRX_FONT_WIDTH_NARROW: FcPatternAddInteger(pattern, FC_WIDTH, 75); break; case GRX_FONT_WIDTH_REGULAR: FcPatternAddInteger(pattern, FC_WIDTH, 100); break; case GRX_FONT_WIDTH_WIDE: FcPatternAddInteger(pattern, FC_WIDTH, 125); break; } if (monospace) { FcPatternAddInteger(pattern, FC_SPACING, 100); } if (lang) { FcPatternAddString(pattern, FC_LANG, (FcChar8 *)lang); } if (script) { FcPatternAddCharSet(pattern, FC_CHARSET, script_to_charset(script)); } { FcChar8 *pattern_str; pattern_str = FcNameUnparse(pattern); g_debug("searching for pattern: %s", pattern_str); free(pattern_str); } FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); match = FcFontMatch(NULL, pattern, &result); if (!match) { // TODO: use result for better error g_set_error_literal(err, GRX_ERROR, GRX_ERROR_FONT_ERROR, "Failed to find match"); FcPatternDestroy(pattern); return NULL; } if (FcPatternGetString(match, FC_FILE, 0, &file) != FcResultMatch) { g_set_error_literal(err, GRX_ERROR, GRX_ERROR_FONT_ERROR, "Failed to get file name"); FcPatternDestroy(match); FcPatternDestroy(pattern); return NULL; } font = grx_font_load_from_file((gchar *)file, err); FcPatternDestroy(match); FcPatternDestroy(pattern); return font; }