Index: src/ports/SkFontHost_win.cpp |
=================================================================== |
--- src/ports/SkFontHost_win.cpp (revision 11929) |
+++ src/ports/SkFontHost_win.cpp (working copy) |
@@ -267,6 +267,8 @@ |
SkAdvancedTypefaceMetrics::PerGlyphInfo, |
const uint32_t*, uint32_t) const SK_OVERRIDE; |
virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE; |
+ virtual int onCharsToGlyphs(const void* chars, Encoding encoding, |
+ uint16_t glyphs[], int glyphCount) const SK_OVERRIDE; |
virtual int onCountGlyphs() const SK_OVERRIDE; |
virtual int onGetUPEM() const SK_OVERRIDE; |
virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE; |
@@ -2079,6 +2081,209 @@ |
return stream; |
} |
+static void bmpCharsToGlyphs(HDC hdc, const WCHAR* bmpChars, int count, uint16_t* glyphs, |
+ bool Ox1FHack) |
+{ |
+ DWORD result = GetGlyphIndicesW(hdc, bmpChars, count, glyphs, GGI_MARK_NONEXISTING_GLYPHS); |
+ if (GDI_ERROR == result) { |
+ for (int i = 0; i < count; ++i) { |
+ glyphs[i] = 0; |
+ } |
+ return; |
+ } |
+ |
+ if (Ox1FHack) { |
+ for (int i = 0; i < count; ++i) { |
+ if (0xFFFF == glyphs[i] || 0x1F == glyphs[i]) { |
+ glyphs[i] = 0; |
+ } |
+ } |
+ } else { |
+ for (int i = 0; i < count; ++i) { |
+ if (0xFFFF == glyphs[i]){ |
+ glyphs[i] = 0; |
+ } |
+ } |
+ } |
+} |
+ |
+static uint16_t nonBmpCharToGlyph(HDC hdc, SCRIPT_CACHE* scriptCache, const WCHAR utf16[2]) { |
+ uint16_t index = 0; |
+ // Use uniscribe to detemine glyph index for non-BMP characters. |
+ static const int numWCHAR = 2; |
+ static const int maxItems = 2; |
+ // MSDN states that this can be NULL, but some things don't work then. |
+ SCRIPT_CONTROL scriptControl = { 0 }; |
+ // Add extra item to SCRIPT_ITEM to work around a bug (now documented). |
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=366643 |
+ SCRIPT_ITEM si[maxItems + 1]; |
+ int numItems; |
+ HRZM(ScriptItemize(utf16, numWCHAR, maxItems, &scriptControl, NULL, si, &numItems), |
+ "Could not itemize character."); |
+ |
+ // Sometimes ScriptShape cannot find a glyph for a non-BMP and returns 2 space glyphs. |
+ static const int maxGlyphs = 2; |
+ SCRIPT_VISATTR vsa[maxGlyphs]; |
+ WORD outGlyphs[maxGlyphs]; |
+ WORD logClust[numWCHAR]; |
+ int numGlyphs; |
+ HRZM(ScriptShape(hdc, scriptCache, utf16, numWCHAR, maxGlyphs, &si[0].a, |
+ outGlyphs, logClust, vsa, &numGlyphs), |
+ "Could not shape character."); |
+ if (1 == numGlyphs) { |
+ index = outGlyphs[0]; |
+ } |
+ return index; |
+} |
+ |
+class SkAutoHDC { |
+public: |
+ SkAutoHDC(const LOGFONT& lf) |
+ : fHdc(::CreateCompatibleDC(NULL)) |
+ , fFont(::CreateFontIndirect(&lf)) |
+ , fSavefont((HFONT)SelectObject(fHdc, fFont)) |
+ { } |
+ ~SkAutoHDC() { |
+ SelectObject(fHdc, fSavefont); |
+ DeleteObject(fFont); |
+ DeleteDC(fHdc); |
+ } |
+ operator HDC() { return fHdc; } |
+private: |
+ HDC fHdc; |
+ HFONT fFont; |
+ HFONT fSavefont; |
+}; |
+ |
+int LogFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, |
+ uint16_t userGlyphs[], int glyphCount) const |
+{ |
+ SkAutoHDC hdc(fLogFont); |
+ |
+ TEXTMETRIC tm; |
+ if (0 == GetTextMetrics(hdc, &tm)) { |
+ call_ensure_accessible(fLogFont); |
+ if (0 == GetTextMetrics(hdc, &tm)) { |
+ tm.tmPitchAndFamily = TMPF_TRUETYPE; |
+ } |
+ } |
+ bool Ox1FHack = !(tm.tmPitchAndFamily & TMPF_VECTOR) /*&& winVer < Vista */; |
+ |
+ SkAutoSTMalloc<256, uint16_t> scratchGlyphs; |
+ uint16_t* glyphs; |
+ if (userGlyphs != NULL) { |
+ glyphs = userGlyphs; |
+ } else { |
+ glyphs = scratchGlyphs.reset(glyphCount); |
+ } |
+ |
+ SCRIPT_CACHE sc = 0; |
+ switch (encoding) { |
+ case SkTypeface::kUTF8_Encoding: { |
+ static const int scratchCount = 256; |
+ WCHAR scratch[scratchCount]; |
+ int glyphIndex = 0; |
+ const char* currentUtf8 = reinterpret_cast<const char*>(chars); |
+ SkUnichar currentChar; |
+ if (glyphCount) { |
+ currentChar = SkUTF8_NextUnichar(¤tUtf8); |
+ } |
+ while (glyphIndex < glyphCount) { |
+ // Try a run of bmp. |
+ int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount); |
+ int runLength = 0; |
+ while (runLength < glyphsLeft && currentChar <= 0xFFFF) { |
+ scratch[runLength] = static_cast<WCHAR>(currentChar); |
+ ++runLength; |
+ if (runLength < glyphsLeft) { |
+ currentChar = SkUTF8_NextUnichar(¤tUtf8); |
+ } |
+ } |
+ if (runLength) { |
+ bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack); |
+ glyphIndex += runLength; |
+ } |
+ |
+ // Try a run of non-bmp. |
+ while (glyphIndex < glyphCount && currentChar > 0xFFFF) { |
+ SkUTF16_FromUnichar(currentChar, reinterpret_cast<uint16_t*>(scratch)); |
+ glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch); |
+ ++glyphIndex; |
+ if (glyphIndex < glyphCount) { |
+ currentChar = SkUTF8_NextUnichar(¤tUtf8); |
+ } |
+ } |
+ } |
+ break; |
+ } |
+ case SkTypeface::kUTF16_Encoding: { |
+ int glyphIndex = 0; |
+ const WCHAR* currentUtf16 = reinterpret_cast<const WCHAR*>(chars); |
+ while (glyphIndex < glyphCount) { |
+ // Try a run of bmp. |
+ int glyphsLeft = glyphCount - glyphIndex; |
+ int runLength = 0; |
+ while (runLength < glyphsLeft && !SkUTF16_IsHighSurrogate(currentUtf16[runLength])) { |
+ ++runLength; |
+ } |
+ if (runLength) { |
+ bmpCharsToGlyphs(hdc, currentUtf16, runLength, &glyphs[glyphIndex], Ox1FHack); |
+ glyphIndex += runLength; |
+ currentUtf16 += runLength; |
+ } |
+ |
+ // Try a run of non-bmp. |
+ while (glyphIndex < glyphCount && SkUTF16_IsHighSurrogate(*currentUtf16)) { |
+ glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, currentUtf16); |
+ ++glyphIndex; |
+ currentUtf16 += 2; |
+ } |
+ } |
+ break; |
+ } |
+ case SkTypeface::kUTF32_Encoding: { |
+ static const int scratchCount = 256; |
+ WCHAR scratch[scratchCount]; |
+ int glyphIndex = 0; |
+ const uint32_t* utf32 = reinterpret_cast<const uint32_t*>(chars); |
+ while (glyphIndex < glyphCount) { |
+ // Try a run of bmp. |
+ int glyphsLeft = SkTMin(glyphCount - glyphIndex, scratchCount); |
+ int runLength = 0; |
+ while (runLength < glyphsLeft && utf32[glyphIndex + runLength] <= 0xFFFF) { |
+ scratch[runLength] = static_cast<WCHAR>(utf32[glyphIndex + runLength]); |
+ ++runLength; |
+ } |
+ if (runLength) { |
+ bmpCharsToGlyphs(hdc, scratch, runLength, &glyphs[glyphIndex], Ox1FHack); |
+ glyphIndex += runLength; |
+ } |
+ |
+ // Try a run of non-bmp. |
+ while (glyphIndex < glyphCount && utf32[glyphIndex] > 0xFFFF) { |
+ SkUTF16_FromUnichar(utf32[glyphIndex], reinterpret_cast<uint16_t*>(scratch)); |
+ glyphs[glyphIndex] = nonBmpCharToGlyph(hdc, &sc, scratch); |
+ ++glyphIndex; |
+ } |
+ } |
+ break; |
+ } |
+ default: |
+ SK_CRASH(); |
+ } |
+ |
+ if (sc) { |
+ ::ScriptFreeCache(&sc); |
+ } |
+ |
+ for (int i = 0; i < glyphCount; ++i) { |
+ if (0 == glyphs[i]) { |
+ return i; |
+ } |
+ } |
+ return glyphCount; |
+} |
+ |
int LogFontTypeface::onCountGlyphs() const { |
HDC hdc = ::CreateCompatibleDC(NULL); |
HFONT font = CreateFontIndirect(&fLogFont); |