bool UniscribeHelper::shape(const UChar* input, int itemLength, int numGlyphs, SCRIPT_ITEM& run, Shaping& shaping) { HFONT hfont = m_hfont; SCRIPT_CACHE* scriptCache = m_scriptCache; SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties; int ascent = m_ascent; HDC tempDC = 0; HGDIOBJ oldFont = 0; HRESULT hr; // When used to fill up glyph pages for simple scripts in non-BMP, // we don't want any font fallback in this class. The simple script // font path can take care of font fallback. bool lastFallbackTried = m_disableFontFallback; bool result; int generatedGlyphs = 0; // In case HFONT passed in ctor cannot render this run, we have to scan // other fonts from the beginning of the font list. resetFontIndex(); // Compute shapes. while (true) { shaping.m_logs.resize(itemLength); shaping.m_glyphs.resize(numGlyphs); shaping.m_visualAttributes.resize(numGlyphs); #ifdef PURIFY // http://code.google.com/p/chromium/issues/detail?id=5309 // Purify isn't able to track the assignments that ScriptShape makes to // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it // writes, will be considered un-initialized data. // // This hack avoid the false-positive UMRs by marking the buffer as // initialized. // // FIXME: A better solution would be to use Purify's API and mark only // the populated range as initialized: // // PurifyMarkAsInitialized( // &shaping.m_glyphs[0], // sizeof(shaping.m_glyphs[0] * generatedGlyphs); ZeroMemory(&shaping.m_glyphs[0], sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size()); #endif // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true // here. Is that what we want? It will display control characters. hr = ScriptShape(tempDC, scriptCache, input, itemLength, numGlyphs, &run.a, &shaping.m_glyphs[0], &shaping.m_logs[0], &shaping.m_visualAttributes[0], &generatedGlyphs); if (hr == E_PENDING) { // Allocate the DC. tempDC = GetDC(0); oldFont = SelectObject(tempDC, hfont); continue; } else if (hr == E_OUTOFMEMORY) { numGlyphs *= 2; continue; } else if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(&shaping.m_glyphs[0], generatedGlyphs, fontProperties))) break; // The current font can't render this run. clear DC and try // next font. if (tempDC) { SelectObject(tempDC, oldFont); ReleaseDC(0, tempDC); tempDC = 0; } if (!m_disableFontFallback && nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) { // The primary font does not support this run. Try next font. // In case of web page rendering, they come from fonts specified in // CSS stylesheets. continue; } else if (!lastFallbackTried) { lastFallbackTried = true; // Generate a last fallback font based on the script of // a character to draw while inheriting size and styles // from the primary font if (!m_logfont.lfFaceName[0]) setLogFontAndStyle(m_hfont, &m_logfont, &m_style); // TODO(jungshik): generic type should come from webkit for // UniscribeHelperTextRun (a derived class used in webkit). const UChar *family = getFallbackFamily(input, itemLength, FontDescription::StandardFamily, 0, 0); bool fontOk = getDerivedFontData(family, m_style, &m_logfont, &ascent, &hfont, &scriptCache); if (!fontOk) { // If this GetDerivedFontData is called from the renderer it // might fail because the sandbox is preventing it from opening // the font files. If we are running in the renderer, // TryToPreloadFont is overridden to ask the browser to preload // the font for us so we can access it. tryToPreloadFont(hfont); // Try again. fontOk = getDerivedFontData(family, m_style, &m_logfont, &ascent, &hfont, &scriptCache); ASSERT(fontOk); } // TODO(jungshik) : Currently GetDerivedHFont always returns a // a valid HFONT, but in the future, I may change it to return 0. ASSERT(hfont); // We don't need a font_properties for the last resort fallback font // because we don't have anything more to try and are forced to // accept empty glyph boxes. If we tried a series of fonts as // 'last-resort fallback', we'd need it, but currently, we don't. continue; } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) { run.a.eScript = SCRIPT_UNDEFINED; continue; } else if (FAILED(hr)) { // Error shaping. generatedGlyphs = 0; result = false; goto cleanup; } } // Sets Windows font data for this run to those corresponding to // a font supporting this run. we don't need to store font_properties // because it's not used elsewhere. shaping.m_hfont = hfont; shaping.m_scriptCache = scriptCache; // The ascent of a font for this run can be different from // that of the primary font so that we need to keep track of // the difference per run and take that into account when calling // ScriptTextOut in |draw|. Otherwise, different runs rendered by // different fonts would not be aligned vertically. shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0; result = true; cleanup: shaping.m_glyphs.resize(generatedGlyphs); shaping.m_visualAttributes.resize(generatedGlyphs); shaping.m_advance.resize(generatedGlyphs); shaping.m_offsets.resize(generatedGlyphs); if (tempDC) { SelectObject(tempDC, oldFont); ReleaseDC(0, tempDC); } // On failure, our logs don't mean anything, so zero those out. if (!result) shaping.m_logs.clear(); return result; }
// Given the desired base font, this will create a SimpleFontData for a specific // font that can be used to render the given range of characters. const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) { // FIXME: Consider passing fontDescription.dominantScript() // to GetFallbackFamily here. FontDescription fontDescription = font.fontDescription(); UChar32 c; UScriptCode script; const wchar_t* family = getFallbackFamily(characters, length, fontDescription.genericFamily(), &c, &script); FontPlatformData* data = 0; if (family) data = getCachedFontPlatformData(font.fontDescription(), AtomicString(family, wcslen(family)), false); // Last resort font list : PanUnicode. CJK fonts have a pretty // large repertoire. Eventually, we need to scan all the fonts // on the system to have a Firefox-like coverage. // Make sure that all of them are lowercased. const static wchar_t* const cjkFonts[] = { L"arial unicode ms", L"ms pgothic", L"simsun", L"gulim", L"pmingliu", L"wenquanyi zen hei", // partial CJK Ext. A coverage but more // widely known to Chinese users. L"ar pl shanheisun uni", L"ar pl zenkai uni", L"han nom a", // Complete CJK Ext. A coverage L"code2000", // Complete CJK Ext. A coverage // CJK Ext. B fonts are not listed here because it's of no use // with our current non-BMP character handling because we use // Uniscribe for it and that code path does not go through here. }; const static wchar_t* const commonFonts[] = { L"tahoma", L"arial unicode ms", L"lucida sans unicode", L"microsoft sans serif", L"palatino linotype", // Six fonts below (and code2000 at the end) are not from MS, but // once installed, cover a very wide range of characters. L"dejavu serif", L"dejavu sasns", L"freeserif", L"freesans", L"gentium", L"gentiumalt", L"ms pgothic", L"simsun", L"gulim", L"pmingliu", L"code2000", }; const wchar_t* const* panUniFonts = 0; int numFonts = 0; if (script == USCRIPT_HAN) { panUniFonts = cjkFonts; numFonts = WTF_ARRAY_LENGTH(cjkFonts); } else { panUniFonts = commonFonts; numFonts = WTF_ARRAY_LENGTH(commonFonts); } // Font returned from GetFallbackFamily may not cover |characters| // because it's based on script to font mapping. This problem is // critical enough for non-Latin scripts (especially Han) to // warrant an additional (real coverage) check with fontCotainsCharacter. int i; for (i = 0; (!data || !fontContainsCharacter(data, family, c)) && i < numFonts; ++i) { family = panUniFonts[i]; data = getCachedFontPlatformData(font.fontDescription(), AtomicString(family, wcslen(family))); } // When i-th font (0-base) in |panUniFonts| contains a character and // we get out of the loop, |i| will be |i + 1|. That is, if only the // last font in the array covers the character, |i| will be numFonts. // So, we have to use '<=" rather than '<' to see if we found a font // covering the character. if (i <= numFonts) return getCachedFontData(data, DoNotRetain); return 0; }
// Given the desired base font, this will create a SimpleFontData for a specific // font that can be used to render the given range of characters. PassRefPtr<SimpleFontData> FontCache::fallbackFontForCharacter( const FontDescription& fontDescription, UChar32 character, const SimpleFontData* originalFontData) { // First try the specified font with standard style & weight. if (fontDescription.style() == FontStyleItalic || fontDescription.weight() >= FontWeightBold) { RefPtr<SimpleFontData> fontData = fallbackOnStandardFontStyle( fontDescription, character); if (fontData) return fontData; } // FIXME: Consider passing fontDescription.dominantScript() // to GetFallbackFamily here. UScriptCode script; const wchar_t* family = getFallbackFamily(character, fontDescription.genericFamily(), &script, m_fontManager.get()); FontPlatformData* data = 0; if (family) { FontFaceCreationParams createByFamily(AtomicString(family, wcslen(family))); data = getFontPlatformData(fontDescription, createByFamily); } // Last resort font list : PanUnicode. CJK fonts have a pretty // large repertoire. Eventually, we need to scan all the fonts // on the system to have a Firefox-like coverage. // Make sure that all of them are lowercased. const static wchar_t* const cjkFonts[] = { L"arial unicode ms", L"ms pgothic", L"simsun", L"gulim", L"pmingliu", L"wenquanyi zen hei", // Partial CJK Ext. A coverage but more widely known to Chinese users. L"ar pl shanheisun uni", L"ar pl zenkai uni", L"han nom a", // Complete CJK Ext. A coverage. L"code2000" // Complete CJK Ext. A coverage. // CJK Ext. B fonts are not listed here because it's of no use // with our current non-BMP character handling because we use // Uniscribe for it and that code path does not go through here. }; const static wchar_t* const commonFonts[] = { L"tahoma", L"arial unicode ms", L"lucida sans unicode", L"microsoft sans serif", L"palatino linotype", // Six fonts below (and code2000 at the end) are not from MS, but // once installed, cover a very wide range of characters. L"dejavu serif", L"dejavu sasns", L"freeserif", L"freesans", L"gentium", L"gentiumalt", L"ms pgothic", L"simsun", L"gulim", L"pmingliu", L"code2000" }; const wchar_t* const* panUniFonts = 0; int numFonts = 0; if (script == USCRIPT_HAN) { panUniFonts = cjkFonts; numFonts = WTF_ARRAY_LENGTH(cjkFonts); } else { panUniFonts = commonFonts; numFonts = WTF_ARRAY_LENGTH(commonFonts); } // Font returned from getFallbackFamily may not cover |character| // because it's based on script to font mapping. This problem is // critical enough for non-Latin scripts (especially Han) to // warrant an additional (real coverage) check with fontCotainsCharacter. int i; for (i = 0; (!data || !data->fontContainsCharacter(character)) && i < numFonts; ++i) { family = panUniFonts[i]; FontFaceCreationParams createByFamily(AtomicString(family, wcslen(family))); data = getFontPlatformData(fontDescription, createByFamily); } // For font fallback we want to match the subpixel behavior of the original // font. Mixing subpixel and non-subpixel in the same text run looks really // odd and causes problems with preferred width calculations. if (data && originalFontData) { const FontPlatformData& platformData = originalFontData->platformData(); data->setMinSizeForAntiAlias(platformData.minSizeForAntiAlias()); data->setMinSizeForSubpixel(platformData.minSizeForSubpixel()); } // When i-th font (0-base) in |panUniFonts| contains a character and // we get out of the loop, |i| will be |i + 1|. That is, if only the // last font in the array covers the character, |i| will be numFonts. // So, we have to use '<=" rather than '<' to see if we found a font // covering the character. if (i <= numFonts) return fontDataFromFontPlatformData(data, DoNotRetain); return nullptr; }