* optionally return their corresponding glyph IDs (if glyphs is not NULL).
*
* @param chars pointer to the array of character codes
- * @param encoding how the characteds are encoded
+ * @param encoding how the characters are encoded
* @param glyphs (optional) returns the corresponding glyph IDs for each
* character code, up to glyphCount values. If a character code is
* not found in the typeface, the corresponding glyph ID will be 0.
virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const = 0;
virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
- int glyphCount) const;
+ int glyphCount) const = 0;
virtual int onCountGlyphs() const = 0;
virtual int onGetUPEM() const = 0;
SkAdvancedTypefaceMetrics::PerGlyphInfo,
const uint32_t*, uint32_t) const SK_OVERRIDE { return NULL; }
virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE { }
+ virtual int onCharsToGlyphs(const void* chars, Encoding encoding,
+ uint16_t glyphs[], int glyphCount) const SK_OVERRIDE {
+ if (glyphs && glyphCount > 0) {
+ sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
+ }
+ return 0;
+ }
virtual int onCountGlyphs() const SK_OVERRIDE { return 0; };
virtual int onGetUPEM() const SK_OVERRIDE { return 0; };
class EmptyLocalizedStrings : public SkTypeface::LocalizedStrings {
SkTypeface* SkTypeface::refMatchingStyle(Style style) const {
return this->onRefMatchingStyle(style);
}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-int SkTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
- uint16_t glyphs[], int glyphCount) const {
- static bool printed = false;
- if (!printed) {
- // Only want to see this message once
- SkDebugf("\n *** onCharsToGlyphs unimplemented ***\n");
- printed = true;
- }
- if (glyphs && glyphCount > 0) {
- sk_bzero(glyphs, glyphCount * sizeof(glyphs[0]));
- }
- return 0;
-}
fProxy->getFontDescriptor(desc, isLocal);
}
+int SkGTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
+ uint16_t glyphs[], int glyphCount) const {
+ return fProxy->charsToGlyphs(chars, encoding, glyphs, glyphCount);
+}
+
int SkGTypeface::onCountGlyphs() const {
return fProxy->countGlyphs();
}
virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) 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;
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;
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);
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;
*isLocalStream = SkToBool(fDWriteFontFileLoader.get());
}
+static SkUnichar next_utf8(const void** chars) {
+ return SkUTF8_NextUnichar((const char**)chars);
+}
+
+static SkUnichar next_utf16(const void** chars) {
+ return SkUTF16_NextUnichar((const uint16_t**)chars);
+}
+
+static SkUnichar next_utf32(const void** chars) {
+ const SkUnichar** uniChars = (const SkUnichar**)chars;
+ SkUnichar uni = **uniChars;
+ *uniChars += 1;
+ return uni;
+}
+
+typedef SkUnichar (*EncodingProc)(const void**);
+
+static EncodingProc find_encoding_proc(SkTypeface::Encoding enc) {
+ static const EncodingProc gProcs[] = {
+ next_utf8, next_utf16, next_utf32
+ };
+ SkASSERT((size_t)enc < SK_ARRAY_COUNT(gProcs));
+ return gProcs[enc];
+}
+
+int DWriteFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding,
+ uint16_t glyphs[], int glyphCount) const
+{
+ if (NULL == glyphs) {
+ EncodingProc next_ucs4_proc = find_encoding_proc(encoding);
+ for (int i = 0; i < glyphCount; ++i) {
+ const SkUnichar c = next_ucs4_proc(&chars);
+ BOOL exists;
+ fDWriteFont->HasCharacter(c, &exists);
+ if (!exists) {
+ return i;
+ }
+ }
+ return glyphCount;
+ }
+
+ switch (encoding) {
+ case SkTypeface::kUTF8_Encoding:
+ case SkTypeface::kUTF16_Encoding: {
+ static const int scratchCount = 256;
+ UINT32 scratch[scratchCount];
+ EncodingProc next_ucs4_proc = find_encoding_proc(encoding);
+ for (int baseGlyph = 0; baseGlyph < glyphCount; baseGlyph += scratchCount) {
+ int glyphsLeft = glyphCount - baseGlyph;
+ int limit = SkTMin(glyphsLeft, scratchCount);
+ for (int i = 0; i < limit; ++i) {
+ scratch[i] = next_ucs4_proc(&chars);
+ }
+ fDWriteFontFace->GetGlyphIndices(scratch, limit, &glyphs[baseGlyph]);
+ }
+ break;
+ }
+ case SkTypeface::kUTF32_Encoding:
+ const UINT32* utf32 = reinterpret_cast<const UINT32*>(chars);
+ fDWriteFontFace->GetGlyphIndices(utf32, glyphCount, glyphs);
+ break;
+ default:
+ SK_CRASH();
+ }
+
+ for (int i = 0; i < glyphCount; ++i) {
+ if (0 == glyphs[i]) {
+ return i;
+ }
+ }
+ return glyphCount;
+}
+
int DWriteFontTypeface::onCountGlyphs() const {
return fDWriteFontFace->GetGlyphCount();
}
#include "Test.h"
#include "SkPaint.h"
#include "SkFontStream.h"
+#include "SkOSFile.h"
#include "SkStream.h"
#include "SkTypeface.h"
#include "SkEndian.h"
}
}
+// The following three are all the same code points in various encodings.
+static uint8_t utf8Chars[] = { 0x61, 0xE4, 0xB8, 0xAD, 0xD0, 0xAF, 0xD7, 0x99, 0xD7, 0x95, 0xF0, 0x9D, 0x84, 0x9E, 0x61 };
+static uint16_t utf16Chars[] = { 0x0061, 0x4E2D, 0x042F, 0x05D9, 0x05D5, 0xD834, 0xDD1E, 0x0061 };
+static uint32_t utf32Chars[] = { 0x00000061, 0x00004E2D, 0x0000042F, 0x000005D9, 0x000005D5, 0x0001D11E, 0x00000061 };
+
+struct CharsToGlyphs_TestData {
+ const void* chars;
+ int charCount;
+ size_t charsByteLength;
+ SkTypeface::Encoding typefaceEncoding;
+ const char* name;
+} static charsToGlyphs_TestData[] = {
+ { utf8Chars, 7, sizeof(utf8Chars), SkTypeface::kUTF8_Encoding, "Simple UTF-8" },
+ { utf16Chars, 7, sizeof(utf16Chars), SkTypeface::kUTF16_Encoding, "Simple UTF-16" },
+ { utf32Chars, 7, sizeof(utf32Chars), SkTypeface::kUTF32_Encoding, "Simple UTF-32" },
+};
+
+// Test that SkPaint::textToGlyphs agrees with SkTypeface::charsToGlyphs.
+static void test_charsToGlyphs(skiatest::Reporter* reporter, SkTypeface* face) {
+ uint16_t paintGlyphIds[256];
+ uint16_t faceGlyphIds[256];
+
+ for (size_t testIndex = 0; testIndex < SK_ARRAY_COUNT(charsToGlyphs_TestData); ++testIndex) {
+ CharsToGlyphs_TestData& test = charsToGlyphs_TestData[testIndex];
+
+ SkPaint paint;
+ paint.setTypeface(face);
+ paint.setTextEncoding((SkPaint::TextEncoding)test.typefaceEncoding);
+ paint.textToGlyphs(test.chars, test.charsByteLength, paintGlyphIds);
+
+ face->charsToGlyphs(test.chars, test.typefaceEncoding, faceGlyphIds, test.charCount);
+
+ for (int i = 0; i < test.charCount; ++i) {
+ SkString name;
+ face->getFamilyName(&name);
+ SkString a;
+ a.appendf("%s, paintGlyphIds[%d] = %d, faceGlyphIds[%d] = %d, face = %s",
+ test.name, i, (int)paintGlyphIds[i], i, (int)faceGlyphIds[i], name.c_str());
+ REPORTER_ASSERT_MESSAGE(reporter, paintGlyphIds[i] == faceGlyphIds[i], a.c_str());
+ }
+ }
+}
+
static void test_fontstream(skiatest::Reporter* reporter,
SkStream* stream, int ttcIndex) {
int n = SkFontStream::GetTableTags(stream, ttcIndex, NULL);
}
static void test_fontstream(skiatest::Reporter* reporter) {
- // TODO: replace when we get a tools/resources/fonts/test.ttc
- const char* name = "/AmericanTypewriter.ttc";
- SkFILEStream stream(name);
+ // This test cannot run if there is no resource path.
+ SkString resourcePath = skiatest::Test::GetResourcePath();
+ if (resourcePath.isEmpty()) {
+ SkDebugf("Could not run fontstream test because resourcePath not specified.");
+ return;
+ }
+ SkString filename = SkOSPath::SkPathJoin(resourcePath.c_str(), "test.ttc");
+
+ SkFILEStream stream(filename.c_str());
if (stream.isValid()) {
test_fontstream(reporter, &stream);
+ } else {
+ SkDebugf("Could not run fontstream test because test.ttc not found.");
}
}
};
for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) {
- SkTypeface* face = SkTypeface::CreateFromName(gNames[i],
- SkTypeface::kNormal);
+ SkAutoTUnref<SkTypeface> face(SkTypeface::CreateFromName(gNames[i], SkTypeface::kNormal));
if (face) {
#ifdef DUMP_TABLES
SkDebugf("%s\n", gNames[i]);
test_tables(reporter, face);
test_unitsPerEm(reporter, face);
test_countGlyphs(reporter, face);
- face->unref();
+ test_charsToGlyphs(reporter, face);
}
}
}
// This test cannot run if there is no resource path.
SkString resourcePath = skiatest::Test::GetResourcePath();
if (resourcePath.isEmpty()) {
+ SkDebugf("Could not run unpremul test because resourcePath not specified.");
return;
}
SkOSFile::Iter iter(resourcePath.c_str());