Reviewed by Darin Adler.
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Jan 2008 02:04:24 +0000 (02:04 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Jan 2008 02:04:24 +0000 (02:04 +0000)
        - fix http://bugs.webkit.org/show_bug.cgi?id=16548
          <rdar://problem/5659452> REGRESSION(r28810): Font style and sizes are weird for Japanese text

        * platform/graphics/win/FontCacheWin.cpp:
        (WebCore::linkedFontEnumProc): Added. This callback is used to fetch
        a valid LOGFONT for a given family.
        (WebCore::getLinkedFonts): Added. Returns a vector of font families
        linked to the given font family by the Windows registry key
        HKLM\Software\...\FontLink\SystemLink. The registry values typically
        differ based on the installed language version of Windows.
        (WebCore::FontCache::getFontDataForCharacters): Changed to not use MLang
        font mapping, which is Windows code page based, except for characters in
        the range U+2000..U+200F. Instead, this function gets the font Uniscribe
        would use for the character. However, that font might not actually
        contain the character, in which case GDI font linking would substitute a
        different font. Therefore, this function walks the linked font list
        until it finds a font that actually contains the character.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@29140 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/graphics/win/FontCacheWin.cpp

index 43d6f39a1fb8cdddb586b519d48731a36a4d11dd..2142f7f9143e4aaa50e7adf7f56482dd5242e61e 100644 (file)
@@ -1,3 +1,25 @@
+2008-01-03  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Darin Adler.
+
+        - fix http://bugs.webkit.org/show_bug.cgi?id=16548
+          <rdar://problem/5659452> REGRESSION(r28810): Font style and sizes are weird for Japanese text
+
+        * platform/graphics/win/FontCacheWin.cpp:
+        (WebCore::linkedFontEnumProc): Added. This callback is used to fetch
+        a valid LOGFONT for a given family.
+        (WebCore::getLinkedFonts): Added. Returns a vector of font families
+        linked to the given font family by the Windows registry key
+        HKLM\Software\...\FontLink\SystemLink. The registry values typically
+        differ based on the installed language version of Windows.
+        (WebCore::FontCache::getFontDataForCharacters): Changed to not use MLang
+        font mapping, which is Windows code page based, except for characters in
+        the range U+2000..U+200F. Instead, this function gets the font Uniscribe
+        would use for the character. However, that font might not actually
+        contain the character, in which case GDI font linking would substitute a
+        different font. Therefore, this function walks the linked font list
+        until it finds a font that actually contains the character.
+
 2008-01-03  Darin Adler  <darin@apple.com>
 
         Reviewed by Mitz.
index 046d2d3ef83f0bdb50f483ec64d10dc68b5cb111..99168738778c5c649f91879257392116e1abcedd 100644 (file)
@@ -31,6 +31,7 @@
 #include "FontCache.h"
 #include "FontData.h"
 #include "Font.h"
+#include "StringHash.h"
 #include <windows.h>
 #include <mlang.h>
 #include <ApplicationServices/ApplicationServices.h>
@@ -72,56 +73,74 @@ static int CALLBACK metaFileEnumProc(HDC hdc, HANDLETABLE* table, CONST ENHMETAR
     return true;
 }
 
-const FontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
+static int CALLBACK linkedFontEnumProc(CONST LOGFONT* logFont, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM hfont)
 {
-    // IMLangFontLink::MapFont Method does what we want.
-    IMLangFontLink2* langFontLink = getFontLinkInterface();
-    if (!langFontLink)
-        return 0;
+    *reinterpret_cast<HFONT*>(hfont) = CreateFontIndirect(logFont);
+    return false;
+}
 
+static const Vector<String>* getLinkedFonts(String& family)
+{
+    static HashMap<String, Vector<String>*> systemLinkMap;
+    Vector<String>* result = systemLinkMap.get(family);
+    if (result)
+        return result;
+
+    result = new Vector<String>;
+    systemLinkMap.set(family, result);
+    HKEY fontLinkKey;
+    if (FAILED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink", 0, KEY_READ, &fontLinkKey)))
+        return result;
+
+    DWORD linkedFontsBufferSize = 0;
+    RegQueryValueEx(fontLinkKey, family.charactersWithNullTermination(), 0, NULL, NULL, &linkedFontsBufferSize);
+    WCHAR* linkedFonts = reinterpret_cast<WCHAR*>(malloc(linkedFontsBufferSize));
+    if (SUCCEEDED(RegQueryValueEx(fontLinkKey, family.charactersWithNullTermination(), 0, NULL, reinterpret_cast<BYTE*>(linkedFonts), &linkedFontsBufferSize))) {
+        unsigned i = 0;
+        unsigned length = linkedFontsBufferSize / sizeof(*linkedFonts);
+        while (i < length) {
+            while (i < linkedFontsBufferSize && linkedFonts[i] != ',')
+                i++;
+            i++;
+            unsigned j = i;
+            while (j < linkedFontsBufferSize && linkedFonts[j])
+                j++;
+            result->append(String(linkedFonts + i, j - i));
+            i = j + 1;
+        }
+    }
+    free(linkedFonts);
+    RegCloseKey(fontLinkKey);
+    return result;
+}
+
+const FontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
+{
     FontData* fontData = 0;
     HDC hdc = GetDC(0);
     HFONT primaryFont = font.primaryFont()->m_font.hfont();
     HGDIOBJ oldFont = SelectObject(hdc, primaryFont);
     HFONT hfont = 0;
 
-    DWORD acpCodePages;
-    langFontLink->CodePageToCodePages(CP_ACP, &acpCodePages);
-
-    DWORD actualCodePages;
-    long cchActual;
-    langFontLink->GetStrCodePages(characters, length, acpCodePages, &actualCodePages, &cchActual);
-    if (cchActual) {
-        // If simplified Chinese is one of the actual code pages, make one call to MapFont() asking for
-        // simplified Chinese only (and ignore the result). This ensures that we get consistent answers
-        // for characters that are in the simplified Chinese code page as well as other code pages and
-        // characters that are exclusively in the simplified Chinese code page.
-        // FIXME: This needs to be done only once per primary font. We could set a bit in the FontPlatformData
-        // indicating that we have done this.
-        const UINT simplifiedChineseCP = 936;
-        UINT codePage;
-        HFONT result;
-        if (actualCodePages && SUCCEEDED(langFontLink->CodePagesToCodePage(actualCodePages, simplifiedChineseCP, &codePage)) && codePage == simplifiedChineseCP) {
-            DWORD simplifiedChineseCodePages;
-            langFontLink->CodePageToCodePages(simplifiedChineseCP, &simplifiedChineseCodePages);
-            langFontLink->MapFont(hdc, simplifiedChineseCodePages, characters[0], &result);
-            langFontLink->ReleaseFont(result);
-        }
-        if (langFontLink->MapFont(hdc, actualCodePages, characters[0], &result) == S_OK) {
-            // Fill in a log font with the returned font from MLang, and then use that to create a new font.
+    // Special-case characters in the range U+2000..U+200F (spaces, zero width joiner and non-joiner,
+    // right to left and left to right marks). Uniscribe does not tell us what font it will use for these
+    // (presumably because it never renders their glyphs), so we use MapFont to find a font that contains
+    // them.
+    if (characters[0] >= 0x2000 && characters[0] <= 0x200F) {
+        IMLangFontLink2* langFontLink = getFontLinkInterface();
+        if (langFontLink) {
+            HFONT mLangFont;
+            langFontLink->MapFont(hdc, 0, characters[0], &mLangFont);
             LOGFONT lf;
-            GetObject(result, sizeof(LOGFONT), &lf);
-            langFontLink->ReleaseFont(result);
+            GetObject(mLangFont, sizeof(LOGFONT), &lf);
+            langFontLink->ReleaseFont(mLangFont);
             hfont = CreateFontIndirect(&lf);
         }
-    }
-
-    if (!hfont) {
-        // Font linking failed but Uniscribe might still be able to find a fallback font.
-        // To find out what Uniscribe would do, we make it draw into a metafile and intercept
+    } else {
+        // To find out what font Uniscribe would use, we make it draw into a metafile and intercept
         // calls to CreateFontIndirect().
         HDC metaFileDc = CreateEnhMetaFile(hdc, NULL, NULL, NULL);
-        SelectObject(metaFileDc, hfont ? hfont : primaryFont);
+        SelectObject(metaFileDc, primaryFont);
 
         bool scriptStringOutSucceeded = false;
         SCRIPT_STRING_ANALYSIS ssa;
@@ -144,12 +163,44 @@ const FontData* FontCache::getFontDataForCharacters(const Font& font, const UCha
         DeleteEnhMetaFile(metaFile);
     }
 
-    if (hfont) {
+    String familyName;
+    const Vector<String>* linkedFonts = 0;
+    unsigned linkedFontIndex = 0;
+    while (hfont) {
         SelectObject(hdc, hfont);
         WCHAR name[LF_FACESIZE];
         GetTextFace(hdc, LF_FACESIZE, name);
+        familyName = name;
+        // Check if the font contains the desired character.
+        static Vector<char, 512> glyphsetBuffer;
+        glyphsetBuffer.resize(GetFontUnicodeRanges(hdc, NULL));
+        GLYPHSET* glyphset = reinterpret_cast<GLYPHSET*>(glyphsetBuffer.data());
+        GetFontUnicodeRanges(hdc, glyphset);
+        // FIXME: Change this to a binary search.
+        unsigned i = 0;
+        while (i < glyphset->cRanges && glyphset->ranges[i].wcLow <= characters[0])
+            i++;
+        if (i && glyphset->ranges[i - 1].wcLow + glyphset->ranges[i - 1].cGlyphs > characters[0])
+            break;
+
+        if (!linkedFonts)
+            linkedFonts = getLinkedFonts(familyName);
+        SelectObject(hdc, oldFont);
+        DeleteObject(hfont);
+        hfont = 0;
+
+        if (linkedFonts->size() <= linkedFontIndex)
+            break;
 
-        String familyName(name);
+        LOGFONT logFont;
+        logFont.lfCharSet = DEFAULT_CHARSET;
+        memcpy(logFont.lfFaceName, linkedFonts->at(linkedFontIndex).characters(), linkedFonts->at(linkedFontIndex).length() * sizeof(WCHAR));
+        logFont.lfFaceName[linkedFonts->at(linkedFontIndex).length()] = 0;
+        EnumFontFamiliesEx(hdc, &logFont, linkedFontEnumProc, reinterpret_cast<LPARAM>(&hfont), 0);
+        linkedFontIndex++;
+    }
+    
+    if (hfont) {
         if (!familyName.isEmpty()) {
             FontPlatformData* result = getCachedFontPlatformData(font.fontDescription(), familyName);
             if (result)