diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 8a5a2cf3..82a7ec8c 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -42,7 +42,8 @@ CFILES += \ windows/window.c CXXFILES += \ - windows/drawtext.cpp + windows/drawtext.cpp \ + windows/dwrite.cpp HFILES += \ windows/area.h \ diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 9c283ea2..488be226 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -1,7 +1,4 @@ // 22 december 2015 -// Before we begin, you may be wondering why this file is C++. -// Simple: is C++ only! Thanks Microsoft! -// And unlike UI Automation which accidentally just forgets the 'struct' and 'enum' tags in places, is a full C++ header file, with class definitions and the use of __uuidof. Oh well :/ #include "uipriv_windows.h" // notes: @@ -11,100 +8,44 @@ // - justficiation (how could I possibly be making this up?!) // - vertical text (SERIOUSLY?! WHAT THE ACTUAL FUCK, MICROSOFT?!?!?!? DID YOU NOT THINK ABOUT THIS THE FIRST TIME, TRYING TO IMPROVE THE INTERNATIONALIZATION OF WINDOWS 7?!?!?! bonus: some parts of MSDN even say 8.1 and up only!) -static IDWriteFactory *dwfactory = NULL; - -HRESULT initDrawText(void) -{ - // TOOD use DWRITE_FACTORY_TYPE_ISOLATED instead? - return DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, - __uuidof (IDWriteFactory), - (IUnknown **) (&dwfactory)); -} - -void uninitDrawText(void) -{ - dwfactory->Release(); -} - struct uiDrawFontFamilies { - IDWriteFontCollection *fonts; - WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; - int userLocaleSuccess; + fontCollection *fc; }; uiDrawFontFamilies *uiDrawListFontFamilies(void) { - uiDrawFontFamilies *ff; - HRESULT hr; + struct uiDrawFontFamilies *ff; - ff = uiNew(uiDrawFontFamilies); - // always get the latest available font information - hr = dwfactory->GetSystemFontCollection(&(ff->fonts), TRUE); - if (hr != S_OK) - logHRESULT("error getting system font collection in uiDrawListFontFamilies()", hr); - ff->userLocaleSuccess = GetUserDefaultLocaleName(ff->userLocale, LOCALE_NAME_MAX_LENGTH); + ff = uiNew(struct uiDrawFontFamilies); + ff->fc = loadFontCollection(); return ff; } uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) { - return ff->fonts->GetFontFamilyCount(); + return ff->fc->fonts->GetFontFamilyCount(); } char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n) { IDWriteFontFamily *family; - IDWriteLocalizedStrings *names; - UINT32 index; - BOOL exists; - UINT32 length; WCHAR *wname; char *name; HRESULT hr; - hr = ff->fonts->GetFontFamily(n, &family); + hr = ff->fc->fonts->GetFontFamily(n, &family); if (hr != S_OK) logHRESULT("error getting font out of collection in uiDrawFontFamiliesFamily()", hr); - hr = family->GetFamilyNames(&names); - if (hr != S_OK) - logHRESULT("error getting names of font out in uiDrawFontFamiliesFamily()", hr); - - // this is complex, but we ignore failure conditions to allow fallbacks - // 1) If the user locale name was successfully retrieved, try it - // 2) If the user locale name was not successfully retrieved, or that locale's string does not exist, or an error occurred, try L"en-us", the US English locale - // 3) And if that fails, assume the first one - // This algorithm is straight from MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/dd368214%28v=vs.85%29.aspx - // For step 2 to work, start by setting hr to S_OK and exists to FALSE. - // TODO does it skip step 2 entirely if step 1 fails? rewrite it to be a more pure conversion of the MSDN code? - hr = S_OK; - exists = FALSE; - if (ff->userLocaleSuccess != 0) - hr = names->FindLocaleName(ff->userLocale, &index, &exists); - if (hr != S_OK || (hr == S_OK && !exists)) - hr = names->FindLocaleName(L"en-us", &index, &exists); - if (!exists) - index = 0; - - hr = names->GetStringLength(index, &length); - if (hr != S_OK) - logHRESULT("error getting length of font name in uiDrawFontFamiliesFamily()", hr); - // GetStringLength() does not include the null terminator, but GetString() does - wname = (WCHAR *) uiAlloc((length + 1) * sizeof (WCHAR), "WCHAR[]"); - hr = names->GetString(index, wname, length + 1); - if (hr != S_OK) - logHRESULT("error getting font name in uiDrawFontFamiliesFamily()", hr); - + wname = fontCollectionFamilyName(ff->fc, family); name = toUTF8(wname); - uiFree(wname); - names->Release(); family->Release(); return name; } void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) { - ff->fonts->Release(); + fontCollectionFree(ff->fc); uiFree(ff); } diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp new file mode 100644 index 00000000..da9ddfa9 --- /dev/null +++ b/windows/dwrite.cpp @@ -0,0 +1,82 @@ +// 14 april 2016 +// Before we begin, you may be wondering why this file is C++. +// Simple: is C++ only! Thanks Microsoft! +// And unlike UI Automation which accidentally just forgets the 'struct' and 'enum' tags in places, is a full C++ header file, with class definitions and the use of __uuidof. Oh well :/ +#include "uipriv_windows.h" + +IDWriteFactory *dwfactory = NULL; + +HRESULT initDrawText(void) +{ + // TOOD use DWRITE_FACTORY_TYPE_ISOLATED instead? + return DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + __uuidof (IDWriteFactory), + (IUnknown **) (&dwfactory)); +} + +void uninitDrawText(void) +{ + dwfactory->Release(); +} + +fontCollection *loadFontCollection(void) +{ + fontCollection *fc; + HRESULT hr; + + fc = uiNew(fontCollection); + // always get the latest available font information + hr = dwfactory->GetSystemFontCollection(&(fc->fonts), TRUE); + if (hr != S_OK) + logHRESULT("error getting system font collection in loadFontCollection()", hr); + fc->userLocaleSuccess = GetUserDefaultLocaleName(fc->userLocale, LOCALE_NAME_MAX_LENGTH); + return fc; +} + +WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family) +{ + IDWriteLocalizedStrings *names; + UINT32 index; + BOOL exists; + UINT32 length; + WCHAR *wname; + HRESULT hr; + + hr = family->GetFamilyNames(&names); + if (hr != S_OK) + logHRESULT("error getting names of font out in fontCollectionFamilyName()", hr); + + // this is complex, but we ignore failure conditions to allow fallbacks + // 1) If the user locale name was successfully retrieved, try it + // 2) If the user locale name was not successfully retrieved, or that locale's string does not exist, or an error occurred, try L"en-us", the US English locale + // 3) And if that fails, assume the first one + // This algorithm is straight from MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/dd368214%28v=vs.85%29.aspx + // For step 2 to work, start by setting hr to S_OK and exists to FALSE. + // TODO does it skip step 2 entirely if step 1 fails? rewrite it to be a more pure conversion of the MSDN code? + hr = S_OK; + exists = FALSE; + if (fc->userLocaleSuccess != 0) + hr = names->FindLocaleName(fc->userLocale, &index, &exists); + if (hr != S_OK || (hr == S_OK && !exists)) + hr = names->FindLocaleName(L"en-us", &index, &exists); + if (!exists) + index = 0; + + hr = names->GetStringLength(index, &length); + if (hr != S_OK) + logHRESULT("error getting length of font name in fontCollectionFamilyName()", hr); + // GetStringLength() does not include the null terminator, but GetString() does + wname = (WCHAR *) uiAlloc((length + 1) * sizeof (WCHAR), "WCHAR[]"); + hr = names->GetString(index, wname, length + 1); + if (hr != S_OK) + logHRESULT("error getting font name in fontCollectionFamilyName()", hr); + + names->Release(); + return wname; +} + +void fontCollectionFree(fontCollection *fc) +{ + fc->fonts->Release(); + uiFree(fc); +} diff --git a/windows/uipriv_windows.h b/windows/uipriv_windows.h index 1ce3c8bd..c0090306 100644 --- a/windows/uipriv_windows.h +++ b/windows/uipriv_windows.h @@ -135,9 +135,24 @@ extern ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd); extern uiDrawContext *newContext(ID2D1RenderTarget *); extern void freeContext(uiDrawContext *); -// drawtext.cpp +// dwrite.cpp +#ifdef __cplusplus +extern IDWriteFactory *dwfactory; +#endif extern HRESULT initDrawText(void); extern void uninitDrawText(void); +#ifdef __cplusplus +struct fontCollection { + IDWriteFontCollection *fonts; + WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; + int userLocaleSuccess; +}; +extern fontCollection *loadFontCollection(void); +extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); +extern void fontCollectionFree(fontCollection *fc); +#endif + +// drawtext.cpp extern void doDrawText(ID2D1RenderTarget *rt, ID2D1Brush *black, double x, double y, uiDrawTextLayout *layout); #ifdef __cplusplus