示例#1
0
bool CDirectWriteRenderer::GetFontMetrics(CDirectWriteFont &Font, FontMetrics *pMetrics)
{
	if (pMetrics == nullptr)
		return false;
	if (m_pRenderTarget == nullptr)
		return false;

	HRESULT hr = E_UNEXPECTED;
	IDWriteTextFormat *pTextFormat = Font.GetTextFormat();

	if (pTextFormat != nullptr) {
		IDWriteFontCollection *pFontCollection;

		hr = pTextFormat->GetFontCollection(&pFontCollection);
		if (SUCCEEDED(hr)) {
			WCHAR szName[256];

			hr = pTextFormat->GetFontFamilyName(szName, lengthof(szName));
			if (SUCCEEDED(hr)) {
				UINT32 Index;
				BOOL fExists;

				hr = pFontCollection->FindFamilyName(szName, &Index, &fExists);
				if (SUCCEEDED(hr)) {
					IDWriteFontFamily *pFontFamily;

					hr = pFontCollection->GetFontFamily(Index, &pFontFamily);
					if (SUCCEEDED(hr)) {
						IDWriteFont *pFont;

						hr = pFontFamily->GetFirstMatchingFont(
							pTextFormat->GetFontWeight(),
							pTextFormat->GetFontStretch(),
							pTextFormat->GetFontStyle(),
							&pFont);
						if (SUCCEEDED(hr)) {
							DWRITE_FONT_METRICS Metrics;

							pFont->GetMetrics(&Metrics);
							const float Ratio = pTextFormat->GetFontSize() / static_cast<float>(Metrics.designUnitsPerEm);
							pMetrics->Ascent = Metrics.ascent * Ratio;
							pMetrics->Descent = Metrics.descent * Ratio;
							pMetrics->LineGap = Metrics.lineGap * Ratio;

							pFont->Release();
						}

						pFontFamily->Release();
					}
				}
			}
		}

		pFontCollection->Release();
	}

	return SUCCEEDED(hr);
}
示例#2
0
bool TextFormatD2D::GetDWPropertiesFromGDIProperties(
	const WCHAR* gdiFamilyName, const bool gdiBold, const bool gdiItalic,
	DWRITE_FONT_WEIGHT& dwFontWeight, DWRITE_FONT_STYLE& dwFontStyle,
	DWRITE_FONT_STRETCH& dwFontStretch, WCHAR* dwFamilyName, UINT dwFamilyNameSize)
{
	bool result = false;
	IDWriteFont* dwFont = CreateDWFontFromGDIFamilyName(gdiFamilyName);
	if (dwFont)
	{
		if (GetFamilyNameFromDWFont(dwFont, dwFamilyName, dwFamilyNameSize))
		{
			dwFontWeight = dwFont->GetWeight();
			if (gdiBold)
			{
				if (dwFontWeight == DWRITE_FONT_WEIGHT_NORMAL)
				{
					dwFontWeight = DWRITE_FONT_WEIGHT_BOLD;
				}
				else if (dwFontWeight < DWRITE_FONT_WEIGHT_ULTRA_BOLD)
				{
					// If 'gdiFamilyName' was e.g. 'Segoe UI Light', |dwFontWeight| wil be equal to
					// DWRITE_FONT_WEIGHT_LIGHT. If |gdiBold| is true in that case, we need to
					// increase the weight a little more for similar results with GDI+.
					// TODO: Is +100 enough?
					dwFontWeight = (DWRITE_FONT_WEIGHT)(dwFontWeight + 100);
				}
			}

			dwFontStyle = dwFont->GetStyle();
			if (gdiItalic && dwFontStyle == DWRITE_FONT_STYLE_NORMAL)
			{
				dwFontStyle = DWRITE_FONT_STYLE_ITALIC;
			}

			dwFontStretch = dwFont->GetStretch();

			result = true;
		}

		dwFont->Release();
	}

	return result;
}
HRESULT GetDWritePropertiesFromGDIProperties(
	IDWriteFactory* factory, const WCHAR* gdiFamilyName, const bool gdiBold, const bool gdiItalic,
	DWRITE_FONT_WEIGHT& dwriteFontWeight, DWRITE_FONT_STYLE& dwriteFontStyle,
	DWRITE_FONT_STRETCH& dwriteFontStretch, WCHAR* dwriteFamilyName, UINT dwriteFamilyNameSize)
{
	HRESULT hr = E_FAIL;
	IDWriteFont* dwriteFont = CreateDWriteFontFromGDIFamilyName(factory, gdiFamilyName);
	if (dwriteFont)
	{
		hr = GetFamilyNameFromDWriteFont(dwriteFont, dwriteFamilyName, dwriteFamilyNameSize);
		if (SUCCEEDED(hr))
		{
			GetPropertiesFromDWriteFont(
				dwriteFont, gdiBold, gdiItalic, &dwriteFontWeight, &dwriteFontStyle, &dwriteFontStretch);
		}

		dwriteFont->Release();
	}

	return hr;
}
IDWriteFont* FindDWriteFontInFontFamilyByGDIFamilyName(
	IDWriteFontFamily* fontFamily, const WCHAR* gdiFamilyName)
{
	const UINT32 fontFamilyFontCount = fontFamily->GetFontCount();
	for (UINT32 j = 0; j < fontFamilyFontCount; ++j)
	{
		IDWriteFont* font;
		HRESULT hr = fontFamily->GetFont(j, &font);
		if (SUCCEEDED(hr))
		{
			WCHAR buffer[LF_FACESIZE];
			hr = GetGDIFamilyNameFromDWriteFont(font, buffer, _countof(buffer));
			if (SUCCEEDED(hr) && _wcsicmp(gdiFamilyName, buffer) == 0)
			{
				return font;
			}

			font->Release();
		}
	}

	return nullptr;
}
示例#5
0
static QFontEngine *loadEngine(int script, const QFontDef &request,
                               HDC fontHdc, int dpi, bool rawMode,
                               const QtFontDesc *desc,
                               const QStringList &family_list)
{
    LOGFONT lf;
    memset(&lf, 0, sizeof(LOGFONT));

    bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fontHdc;

    HDC hdc = shared_dc();
    QString font_name = desc != 0 ? desc->family->name : request.family;

    if (useDevice) {
        hdc = fontHdc;
        font_name = request.family;
    }

    bool stockFont = false;
    bool preferClearTypeAA = false;

    HFONT hfont = 0;


#if !defined(QT_NO_DIRECTWRITE)
    bool useDirectWrite = (request.hintingPreference == QFont::PreferNoHinting)
                       || (request.hintingPreference == QFont::PreferVerticalHinting);
    IDWriteFont *directWriteFont = 0;
#else
    bool useDirectWrite = false;
#endif

    if (rawMode) {                        // will choose a stock font
        int f, deffnt = SYSTEM_FONT;
        QString fam = desc != 0 ? desc->family->name.toLower() : request.family.toLower();
        if (fam == QLatin1String("default"))
            f = deffnt;
        else if (fam == QLatin1String("system"))
            f = SYSTEM_FONT;
#ifndef Q_WS_WINCE
        else if (fam == QLatin1String("system_fixed"))
            f = SYSTEM_FIXED_FONT;
        else if (fam == QLatin1String("ansi_fixed"))
            f = ANSI_FIXED_FONT;
        else if (fam == QLatin1String("ansi_var"))
            f = ANSI_VAR_FONT;
        else if (fam == QLatin1String("device_default"))
            f = DEVICE_DEFAULT_FONT;
        else if (fam == QLatin1String("oem_fixed"))
            f = OEM_FIXED_FONT;
#endif
        else if (fam[0] == QLatin1Char('#'))
            f = fam.right(fam.length()-1).toInt();
        else
            f = deffnt;
        hfont = (HFONT)GetStockObject(f);
        if (!hfont) {
            qErrnoWarning("QFontEngine::loadEngine: GetStockObject failed");
            hfont = systemFont();
        }
        stockFont = true;
    } else {

        int hint = FF_DONTCARE;
        switch (request.styleHint) {
            case QFont::Helvetica:
                hint = FF_SWISS;
                break;
            case QFont::Times:
                hint = FF_ROMAN;
                break;
            case QFont::Courier:
                hint = FF_MODERN;
                break;
            case QFont::OldEnglish:
                hint = FF_DECORATIVE;
                break;
            case QFont::System:
                hint = FF_MODERN;
                break;
            default:
                break;
        }

        lf.lfHeight = -qRound(request.pixelSize);
        lf.lfWidth                = 0;
        lf.lfEscapement        = 0;
        lf.lfOrientation        = 0;
        if (desc == 0 || desc->style->key.weight == 50)
            lf.lfWeight = FW_DONTCARE;
        else
            lf.lfWeight = (desc->style->key.weight*900)/99;
        lf.lfItalic         = (desc != 0 && desc->style->key.style != QFont::StyleNormal);
        lf.lfCharSet        = DEFAULT_CHARSET;

        int strat = OUT_DEFAULT_PRECIS;
        if (request.styleStrategy & QFont::PreferBitmap) {
            strat = OUT_RASTER_PRECIS;
#ifndef Q_WS_WINCE
        } else if (request.styleStrategy & QFont::PreferDevice) {
            strat = OUT_DEVICE_PRECIS;
        } else if (request.styleStrategy & QFont::PreferOutline) {
            strat = OUT_OUTLINE_PRECIS;
        } else if (request.styleStrategy & QFont::ForceOutline) {
            strat = OUT_TT_ONLY_PRECIS;
#endif
        }

        lf.lfOutPrecision   = strat;

        int qual = DEFAULT_QUALITY;

        if (request.styleStrategy & QFont::PreferMatch)
            qual = DRAFT_QUALITY;
#ifndef Q_WS_WINCE
        else if (request.styleStrategy & QFont::PreferQuality)
            qual = PROOF_QUALITY;
#endif

        if (request.styleStrategy & QFont::PreferAntialias) {
            if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP) {
                qual = CLEARTYPE_QUALITY;
                preferClearTypeAA = true;
            } else {
                qual = ANTIALIASED_QUALITY;
            }
        } else if (request.styleStrategy & QFont::NoAntialias) {
            qual = NONANTIALIASED_QUALITY;
        }

        lf.lfQuality        = qual;

        lf.lfClipPrecision  = CLIP_DEFAULT_PRECIS;
        lf.lfPitchAndFamily = DEFAULT_PITCH | hint;

        QString fam = font_name;

        if(fam.isEmpty())
            fam = QLatin1String("MS Sans Serif");

        if ((fam == QLatin1String("MS Sans Serif"))
            && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) {
            fam = QLatin1String("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale
        }
        if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap))
            fam = QLatin1String("Courier New");

        memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32));  // 32 = Windows hard-coded

        hfont = CreateFontIndirect(&lf);
        if (!hfont)
            qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed");

        stockFont = (hfont == 0);
        bool ttf = false;
        int avWidth = 0;
        BOOL res;
        HGDIOBJ oldObj = SelectObject(hdc, hfont);

        TEXTMETRIC tm;
        res = GetTextMetrics(hdc, &tm);
        avWidth = tm.tmAveCharWidth;
        ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE;
        SelectObject(hdc, oldObj);

        if (!ttf || !useDirectWrite) {
            useDirectWrite = false;

            if (hfont && (!ttf || request.stretch != 100)) {
                DeleteObject(hfont);
                if (!res)
                    qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed");
                lf.lfWidth = avWidth * request.stretch/100;
                hfont = CreateFontIndirect(&lf);
                if (!hfont)
                    qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed");
            }

#ifndef Q_WS_WINCE
            if (hfont == 0) {
                hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
                stockFont = true;
            }
#else
            if (hfont == 0) {
                hfont = (HFONT)GetStockObject(SYSTEM_FONT);
                stockFont = true;
            }
#endif

        }

#if !defined(QT_NO_DIRECTWRITE)
        else {
            // Default to false for DirectWrite (and re-enable once/if everything
            // turns out okay)
            useDirectWrite = false;

            QFontDatabasePrivate *db = privateDb();
            if (db->directWriteFactory == 0) {
                HRESULT hr = DWriteCreateFactory(
                            DWRITE_FACTORY_TYPE_SHARED,
                            __uuidof(IDWriteFactory),
                            reinterpret_cast<IUnknown **>(&db->directWriteFactory)
                            );
                if (FAILED(hr)) {
                    qErrnoWarning("QFontEngine::loadEngine: DWriteCreateFactory failed");
                } else {
                    hr = db->directWriteFactory->GetGdiInterop(&db->directWriteGdiInterop);
                    if (FAILED(hr))
                        qErrnoWarning("QFontEngine::loadEngine: GetGdiInterop failed");
                }
            }

            if (db->directWriteGdiInterop != 0) {
                QString nameSubstitute = fontNameSubstitute(QString::fromWCharArray(lf.lfFaceName));
                memcpy(lf.lfFaceName, nameSubstitute.utf16(),
                       sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE));

                HRESULT hr = db->directWriteGdiInterop->CreateFontFromLOGFONT(
                            &lf,
                            &directWriteFont);
                if (FAILED(hr)) {
#ifndef QT_NO_DEBUG
                    qErrnoWarning("QFontEngine::loadEngine: CreateFontFromLOGFONT failed "
                                  "for %ls (0x%lx)",
                                  lf.lfFaceName, hr);
#endif
                } else {
                    DeleteObject(hfont);
                    useDirectWrite = true;
                }
            }
        }
#endif

    }

    QFontEngine *fe = 0;
    if (!useDirectWrite)  {
        QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf);
        if (preferClearTypeAA)
            few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask;

        // Also check for OpenType tables when using complex scripts
        // ### TODO: This only works for scripts that require OpenType. More generally
        // for scripts that do not require OpenType we should just look at the list of
        // supported writing systems in the font's OS/2 table.
        if (scriptRequiresOpenType(script)) {
            HB_Face hbFace = few->harfbuzzFace();
            if (!hbFace || !hbFace->supported_scripts[script]) {
                FM_DEBUG("  OpenType support missing for script\n");
                delete few;
                return 0;
            }
        }

        initFontInfo(few, request, fontHdc, dpi);
        fe = few;
    }

#if !defined(QT_NO_DIRECTWRITE)
    else {
        QFontDatabasePrivate *db = privateDb();

        IDWriteFontFace *directWriteFontFace = NULL;
        HRESULT hr = directWriteFont->CreateFontFace(&directWriteFontFace);
        if (SUCCEEDED(hr)) {
            QFontEngineDirectWrite *fedw = new QFontEngineDirectWrite(db->directWriteFactory,
                                                                      directWriteFontFace,
                                                                      request.pixelSize);

            initFontInfo(fedw, request, dpi, directWriteFont);

            fe = fedw;
        } else {
            qErrnoWarning(hr, "QFontEngine::loadEngine: CreateFontFace failed");
        }
    }

    if (directWriteFont != 0)
        directWriteFont->Release();
#endif

    if(script == QUnicodeTables::Common
       && !(request.styleStrategy & QFont::NoFontMerging)
       && desc != 0
       && !(desc->family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) {
        if(!tryFonts) {
            LANGID lid = GetUserDefaultLangID();
            switch( lid&0xff ) {
            case LANG_CHINESE: // Chinese (Taiwan)
                if ( lid == 0x0804 ) // Taiwan
                    tryFonts = ch_TW_tryFonts;
                else
                    tryFonts = ch_CN_tryFonts;
                break;
            case LANG_JAPANESE:
                tryFonts = jp_tryFonts;
                break;
            case LANG_KOREAN:
                tryFonts = kr_tryFonts;
                break;
            default:
                tryFonts = other_tryFonts;
                break;
            }
        }
        QStringList fm = QFontDatabase().families();
        QStringList list = family_list;
        const char **tf = tryFonts;
        while(tf && *tf) {
            if(fm.contains(QLatin1String(*tf)))
                list << QLatin1String(*tf);
            ++tf;
        }
        QFontEngine *mfe = new QFontEngineMultiWin(fe, list);
        mfe->fontDef = fe->fontDef;
        fe = mfe;
    }
    return fe;
}
void TextFormatD2D::SetProperties(
	const WCHAR* fontFamily, int size, bool bold, bool italic,
	const FontCollection* fontCollection)
{
	auto fontCollectionD2D = (FontCollectionD2D*)fontCollection;

	Dispose();

	WCHAR dwriteFamilyName[LF_FACESIZE];
	DWRITE_FONT_WEIGHT dwriteFontWeight =
		bold ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_REGULAR;
	DWRITE_FONT_STYLE dwriteFontStyle =
		italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL;
	DWRITE_FONT_STRETCH dwriteFontStretch = DWRITE_FONT_STRETCH_NORMAL;
	const float dwriteFontSize = size * (4.0f / 3.0f);

	// |fontFamily| uses the GDI/GDI+ font naming convention so try to create DirectWrite font
	// using the GDI family name and then create a text format using the DirectWrite family name
	// obtained from it.
	HRESULT hr = Util::GetDWritePropertiesFromGDIProperties(
		CanvasD2D::c_DWFactory.Get(), fontFamily, bold, italic, dwriteFontWeight, dwriteFontStyle,
		dwriteFontStretch, dwriteFamilyName, _countof(dwriteFamilyName));
	if (SUCCEEDED(hr))
	{
		hr = CanvasD2D::c_DWFactory->CreateTextFormat(
			dwriteFamilyName,
			nullptr,
			dwriteFontWeight,
			dwriteFontStyle,
			dwriteFontStretch,
			dwriteFontSize,
			L"",
			&m_TextFormat);
	}

	if (FAILED(hr))
	{
		IDWriteFontCollection* dwriteFontCollection = nullptr;

		// If |fontFamily| is not in the system collection, use the font collection from
		// |fontCollectionD2D| if possible.
		if (!Util::IsFamilyInSystemFontCollection(CanvasD2D::c_DWFactory.Get(), fontFamily) &&
			(fontCollectionD2D && fontCollectionD2D->InitializeCollection()))
		{
			IDWriteFont* dwriteFont = Util::FindDWriteFontInFontCollectionByGDIFamilyName(
				fontCollectionD2D->m_Collection, fontFamily);
			if (dwriteFont)
			{
				hr = Util::GetFamilyNameFromDWriteFont(
					dwriteFont, dwriteFamilyName, _countof(dwriteFamilyName));
				if (SUCCEEDED(hr))
				{
					fontFamily = dwriteFamilyName;
					Util::GetPropertiesFromDWriteFont(
						dwriteFont, bold, italic, &dwriteFontWeight, &dwriteFontStyle,
						&dwriteFontStretch);
				}

				dwriteFont->Release();
			}

			dwriteFontCollection = fontCollectionD2D->m_Collection;
		}

		// Fallback in case above fails.
		hr = CanvasD2D::c_DWFactory->CreateTextFormat(
			fontFamily,
			dwriteFontCollection,
			dwriteFontWeight,
			dwriteFontStyle,
			dwriteFontStretch,
			dwriteFontSize,
			L"",
			&m_TextFormat);
	}

	if (SUCCEEDED(hr))
	{
		SetHorizontalAlignment(GetHorizontalAlignment());
		SetVerticalAlignment(GetVerticalAlignment());

		// Get the family name to in case CreateTextFormat() fallbacked on some other family name.
		hr = m_TextFormat->GetFontFamilyName(dwriteFamilyName, _countof(dwriteFamilyName));
		if (FAILED(hr)) return;

		Microsoft::WRL::ComPtr<IDWriteFontCollection> collection;
		Microsoft::WRL::ComPtr<IDWriteFontFamily> fontFamily;
		UINT32 familyNameIndex;
		BOOL exists;
		if (FAILED(m_TextFormat->GetFontCollection(collection.GetAddressOf())) ||
			FAILED(collection->FindFamilyName(dwriteFamilyName, &familyNameIndex, &exists)) ||
			FAILED(collection->GetFontFamily(familyNameIndex, fontFamily.GetAddressOf())))
		{
			return;
		}

		Microsoft::WRL::ComPtr<IDWriteFont> font;
		hr = fontFamily->GetFirstMatchingFont(
			m_TextFormat->GetFontWeight(),
			m_TextFormat->GetFontStretch(),
			m_TextFormat->GetFontStyle(),
			font.GetAddressOf());
		if (FAILED(hr)) return;

		DWRITE_FONT_METRICS fmetrics;
		font->GetMetrics(&fmetrics);

		// GDI+ compatibility: GDI+ adds extra padding below the string when |m_AccurateText| is
		// |false|. The bottom padding seems to be based on the font metrics so we can calculate it
		// once and keep using it regardless of the actual string. In some cases, GDI+ also adds
		// the line gap to the overall height so we will store it as well.
		const float pixelsPerDesignUnit =  dwriteFontSize / (float)fmetrics.designUnitsPerEm;
		m_ExtraHeight =
			(((float)fmetrics.designUnitsPerEm / 8.0f) - fmetrics.lineGap) * pixelsPerDesignUnit;
		m_LineGap = fmetrics.lineGap * pixelsPerDesignUnit;
	}
	else
	{
		Dispose();
	}
}
int main(void)
{
	int argc;
	LPWSTR *argv;
	WCHAR *fontname, *string;
	int len;
	HDC dc;
	HFONT font, prevfont;
	IDWriteFontCollection *sysfc;
	UINT32 index;
	BOOL exists;
	IDWriteFontFamily *dwfamily;
	IDWriteFont *dwfont;
	IDWriteFontFace *dwface;
	featurePreparer *features;
	HRESULT hr;

	// TODO would using wmain() be adequate?
	argv = CommandLineToArgvW(GetCommandLineW(), &argc);
	if (argv == NULL)
		dieLE("error getting command-line arguments");
	if (argc != 3) {
		fprintf(stderr, "usage: %ws font string\n", argv[0]);
		return 1;
	}
	fontname = argv[1];
	string = argv[2];
	len = wcslen(string);

	// DirectWrite requires COM
	hr = CoInitialize(NULL);
	if (hr != S_OK)
		die("error initializing COM", hr);
	// Uniscribe requires a device context with the font to use
	dc = GetDC(NULL);
	if (dc == NULL)
		dieLE("error getting screen HDC for Uniscribe");
	// TODO DEFAULT_CHARSET might affect the results we get
	font = CreateFontW(0, 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontname);
	if (font == NULL)
		dieLE("error creating font for Uniscribe");
	prevfont = (HFONT) SelectObject(dc, font);
	if (prevfont == NULL)
		dieLE("error selecting font into HDC for Uniscribe");
	// and initialize DirectWrite
	hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
		__uuidof (IDWriteFactory),
		(IUnknown **) (&dwfactory));
	if (hr != S_OK)
		die("error initializing DirectWrite", hr);
	// and load the font *there*
	hr = dwfactory->GetSystemFontCollection(&sysfc, TRUE);
	if (hr != S_OK)
		die("error loading DirectWrite system font collection", hr);
	hr = sysfc->FindFamilyName(fontname, &index, &exists);
	if (hr != S_OK)
		die("error finding DirectWrite font family", hr);
	if (!exists)
		die("font not found in DirectWrite system font collection", E_FAIL);
	hr = sysfc->GetFontFamily(index, &dwfamily);
	if (hr != S_OK)
		die("error loading DirectWrite font family", hr);
	hr = dwfamily->GetFirstMatchingFont(DWRITE_FONT_WEIGHT_NORMAL,
		DWRITE_FONT_STRETCH_NORMAL,
		DWRITE_FONT_STYLE_NORMAL,
		&dwfont);
	if (hr != S_OK)
		die("error loading DirectWrite font object", hr);
	hr = dwfont->CreateFontFace(&dwface);
	if (hr != S_OK)
		die("error creating DirectWrite font face", hr);

	// first, uniscribe only; no features are used
//	uniscribeTest(dc, string, len, NULL,
//		doScriptItemize, doScriptShape, "Uniscribe");

	// next, unprepared features (NULL values)
	features = new featurePreparer;
//features->add('z','e','r','o',1);
//features->add('f','r','a','c',1);features->prepare(len);
//	uniscribeTest(dc, string, len, features,
//		doScriptItemizeOpenType, doScriptShapeOpenType, "Uniscribe OpenType");
	directwriteAnalyzerTest(dwface, string, len, features);
	delete features;

	dwface->Release();
	dwfont->Release();
	dwfamily->Release();
	sysfc->Release();
	dwfactory->Release();
	SelectObject(dc, prevfont);
	DeleteObject(font);
	ReleaseDC(NULL, dc);
	CoUninitialize();
	return 0;
}