summaryrefslogtreecommitdiffstats
path: root/skia/ext
diff options
context:
space:
mode:
authorevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-08 18:37:56 +0000
committerevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-08 18:37:56 +0000
commitdde18a7b322825251044240b6af3ae6ace2309dd (patch)
treee5ec2894bfa4a68e5a7a5a7fdacc414e6897e669 /skia/ext
parent7e5bee5034d8fdcab1ac7f15726bb08227af27fd (diff)
downloadchromium_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.cpp111
-rw-r--r--skia/ext/SkFontHost_fontconfig_direct.h4
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_;