diff options
author | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-08 18:37:56 +0000 |
---|---|---|
committer | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-08 18:37:56 +0000 |
commit | dde18a7b322825251044240b6af3ae6ace2309dd (patch) | |
tree | e5ec2894bfa4a68e5a7a5a7fdacc414e6897e669 /skia/ext | |
parent | 7e5bee5034d8fdcab1ac7f15726bb08227af27fd (diff) | |
download | chromium_src-dde18a7b322825251044240b6af3ae6ace2309dd.zip chromium_src-dde18a7b322825251044240b6af3ae6ace2309dd.tar.gz chromium_src-dde18a7b322825251044240b6af3ae6ace2309dd.tar.bz2 |
linux: handle basic metric-compatibile font fallback
When a page CSS asks for:
font-family: Arial, sans-serif;
and you don't have Arial, fontconfig tries to provide a substitute.
However, as far as I can tell there is no way to distinguish fontconfig picking
Liberation Sans (a metric-equivalent substitute for Arial) or it picking a bad
substitute like Times New Roman. So historically we didn't allow fontconfig to
do any fallback at all. (In this example, we would then match attempt to match
sans-serif, which as a last resort we do allow fontconfig to do fallback on, so
it could pick e.g. DejaVu Sans or any other font matching "sans".)
Frustratingly, fontconfig knows whether a font substitute is an equivalent font
or just a last-resort fallback one -- in the XML files the equivalents are
marked with 'binding="same"' -- but I played around with the API for a few
hours and couldn't figure out any way to get this information out of it.
So I hardcode a list of equivalent fonts. Ugh. This fixes some sites where
they specify Arial and then have pixel-width-specified page layout that breaks
when you use any other sans-serif font.
BUG=18159,32005,33125,others I've since forgotten about
TEST=uninstall Arial; visit one of the sites mentioned in the bug list
Review URL: http://codereview.chromium.org/668127
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40912 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia/ext')
-rw-r--r-- | skia/ext/SkFontHost_fontconfig_direct.cpp | 111 | ||||
-rw-r--r-- | skia/ext/SkFontHost_fontconfig_direct.h | 4 |
2 files changed, 91 insertions, 24 deletions
diff --git a/skia/ext/SkFontHost_fontconfig_direct.cpp b/skia/ext/SkFontHost_fontconfig_direct.cpp index a055c2f..ed92387 100644 --- a/skia/ext/SkFontHost_fontconfig_direct.cpp +++ b/skia/ext/SkFontHost_fontconfig_direct.cpp @@ -27,6 +27,76 @@ FontConfigDirect::FontConfigDirect() FcInit(); } +bool FontConfigDirect::IsMetricCompatibleReplacement(const char* font_a, + const char* font_b) +{ + // It would be nice for fontconfig to tell us whether a given suggested + // replacement is a "strong" match (that is, an equivalent font) or + // a "weak" match (that is, fontconfig's next-best attempt at finding a + // substitute). However, I played around with the fontconfig API for + // a good few hours and could not make it reveal this information. + // + // So instead, we hardcode. These are from + // /etc/fonts/conf.d/30-metric-aliases.conf on my Ubuntu Karmic + // system. + + // We represent the data with a table. Two names with the same + // id are in the same class. + struct FontEquivClass { + char id; + const char name[20]; + }; + static const FontEquivClass kFontEquivClasses[] = { + { 0, "Arial" }, + { 0, "Liberation Sans" }, + { 0, "Albany" }, + { 0, "Albany Amt" }, + + { 1, "Times New Roman" }, + { 1, "Liberation Serif" }, + { 1, "Thorndale" }, + { 1, "Thorndale AMT" }, + + // Note that Liberation Mono doesn't much *look* like Courier New, + // but it's reportedly metric-compatible. + { 2, "Courier New" }, + { 2, "Liberation Mono" }, + { 2, "Cumberland" }, + { 2, "Cumberland AMT" }, + + { 3, "Helvetica" }, + { 3, "Nimbus Sans L" }, + + { 4, "Times" }, + { 4, "Nimbus Roman No9 L" }, + + { 5, "Courier" }, + { 5, "Nimbus Mono L" }, + }; + static const size_t kClassCount = + sizeof(kFontEquivClasses)/sizeof(kFontEquivClasses[0]); + + int class_a = -1; + for (size_t i = 0; i < kClassCount; ++i) { + if (strcasecmp(kFontEquivClasses[i].name, font_a) == 0) { + class_a = kFontEquivClasses[i].id; + break; + } + } + if (class_a == -1) + return false; + + int class_b = -1; + for (size_t i = 0; i < kClassCount; ++i) { + if (strcasecmp(kFontEquivClasses[i].name, font_b) == 0) { + class_b = kFontEquivClasses[i].id; + break; + } + } + + return class_a == class_b; +} + // ----------------------------------------------------------------------------- // Normally we only return exactly the font asked for. In last-resort cases, // the request is for one of the basic font names "Sans", "Serif" or @@ -54,7 +124,6 @@ bool FontConfigDirect::Match(std::string* result_family, SkAutoMutexAcquire ac(mutex_); FcPattern* pattern = FcPatternCreate(); - FcValue fcvalue; if (fileid_valid) { const std::map<unsigned, std::string>::const_iterator i = fileid_to_filename_.find(fileid); @@ -63,29 +132,21 @@ bool FontConfigDirect::Match(std::string* result_family, return false; } - fcvalue.type = FcTypeString; - fcvalue.u.s = (FcChar8*) i->second.c_str(); - FcPatternAdd(pattern, FC_FILE, fcvalue, 0); + FcPatternAddString(pattern, FC_FILE, (FcChar8*) i->second.c_str()); } if (!family.empty()) { - fcvalue.type = FcTypeString; - fcvalue.u.s = (FcChar8*) family.c_str(); - FcPatternAdd(pattern, FC_FAMILY, fcvalue, 0); + FcPatternAddString(pattern, FC_FAMILY, (FcChar8*) family.c_str()); } - fcvalue.type = FcTypeInteger; - fcvalue.u.i = is_bold && *is_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL; - FcPatternAdd(pattern, FC_WEIGHT, fcvalue, 0); - - fcvalue.type = FcTypeInteger; - fcvalue.u.i = is_italic && *is_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN; - FcPatternAdd(pattern, FC_SLANT, fcvalue, 0); - - fcvalue.type = FcTypeBool; - fcvalue.u.b = FcTrue; - FcPatternAdd(pattern, FC_SCALABLE, fcvalue, 0); + 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(0, pattern, FcMatchPattern); + FcConfigSubstitute(NULL, pattern, FcMatchPattern); FcDefaultSubstitute(pattern); // Font matching: @@ -158,13 +219,13 @@ bool FontConfigDirect::Match(std::string* result_family, } if (!IsFallbackFontAllowed(family)) { - bool family_names_match = false; + bool acceptable_substitute = false; for (int id = 0; id < 255; ++id) { FcChar8* post_match_family; if (FcPatternGetString(match, FC_FAMILY, id, &post_match_family) != FcResultMatch) break; - family_names_match = + acceptable_substitute = family.empty() ? true : (strcasecmp((char *)post_config_family, @@ -175,11 +236,13 @@ bool FontConfigDirect::Match(std::string* result_family, // post_match_family: "Bitstream Vera Sans" // -> We should treat this case as a good match. strcasecmp(family.c_str(), - (char *)post_match_family) == 0); - if (family_names_match) + (char *)post_match_family) == 0) || + IsMetricCompatibleReplacement(family.c_str(), + (char*)post_match_family); + if (acceptable_substitute) break; } - if (!family.empty() && !family_names_match) { + if (!acceptable_substitute) { FcPatternDestroy(pattern); FcFontSetDestroy(font_set); return false; diff --git a/skia/ext/SkFontHost_fontconfig_direct.h b/skia/ext/SkFontHost_fontconfig_direct.h index 56bea34..3f9475d 100644 --- a/skia/ext/SkFontHost_fontconfig_direct.h +++ b/skia/ext/SkFontHost_fontconfig_direct.h @@ -35,6 +35,10 @@ class FontConfigDirect : public FontConfigInterface { virtual int Open(unsigned fileid); private: + // Return true if font_a and font_b are completely interchangeable; + // e.g., there exist free clones of certain well-known fonts. + bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b); + SkMutex mutex_; std::map<unsigned, std::string> fileid_to_filename_; std::map<std::string, unsigned> filename_to_fileid_; |