blob: fd820cdda5ba63178f5dce1088a66fef016fa6fa [file] [log] [blame] [edit]
Use newer API to get the Windows fonts directory, and convert from
UTF-16 to UTF-8, assuming that the returned pathname will be treated
as UTF-8 anyway, and converted to UTF-16 before opening fonts in it
etc.
But probaly fontconfig asumes system codepage for pathnames it uses on
Windows all over the place anyway, so things are really messed up. Oh
well.
--- src/fcint.h
+++ src/fcint.h
@@ -69,10 +69,6 @@
#ifdef _WIN32
# include "fcwindows.h"
-typedef UINT (WINAPI *pfnGetSystemWindowsDirectory) (LPSTR, UINT);
-typedef HRESULT (WINAPI *pfnSHGetFolderPathA) (HWND, int, HANDLE, DWORD, LPSTR);
-extern pfnGetSystemWindowsDirectory pGetSystemWindowsDirectory;
-extern pfnSHGetFolderPathA pSHGetFolderPathA;
# define FC_SEARCH_PATH_SEPARATOR ';'
# define FC_DIR_SEPARATOR '\\'
# define FC_DIR_SEPARATOR_S "\\"
--- src/fccfg.c
+++ src/fccfg.c
@@ -658,6 +658,9 @@
FcConfigMapFontPath (FcConfig *config,
const FcChar8 *path)
{
+#ifdef _WIN32
+ return 0;
+#else
FcStrList *list;
FcChar8 *dir;
const FcChar8 *map, *rpath;
@@ -687,12 +687,16 @@
retval[len] = 0;
}
return retval;
+#endif
}
const FcChar8 *
FcConfigMapSalt (FcConfig *config,
const FcChar8 *path)
{
+#ifdef _WIN32
+ return 0;
+#else
FcStrList *list;
FcChar8 *dir;
@@ -707,6 +707,7 @@
return NULL;
return FcStrTripleThird (dir);
+#endif
}
FcBool
--- src/fcxml.c
+++ src/fcxml.c
@@ -58,11 +58,10 @@
#ifdef _WIN32
# include <mbstring.h>
+# include <wchar.h>
+# include <Shlobj.h>
extern FcChar8 fontconfig_instprefix[];
-pfnGetSystemWindowsDirectory pGetSystemWindowsDirectory = NULL;
-pfnSHGetFolderPathA pSHGetFolderPathA = NULL;
-static void
-_ensureWin32GettersReady ();
+extern char* appx_fonts_get_dirs();
#endif
static void
@@ -1264,6 +1264,7 @@
#endif
FcChar8 *parent = NULL, *retval = NULL;
FcStrSet *e = NULL;
+ FcBool dontInsertRetval = FcFalse;
if (prefix) {
if (FcStrCmp (prefix, (const FcChar8 *)"xdg") == 0) {
@@ -1330,23 +1330,42 @@
strcat ((char *)path, "\\..\\share\\fonts");
} else if (strcmp ((const char *)path, "WINDOWSUSERFONTDIR") == 0) {
path = buffer;
- if (!(pSHGetFolderPathA && SUCCEEDED (pSHGetFolderPathA (NULL, /* CSIDL_LOCAL_APPDATA */ 28, NULL, 0, (char *)buffer)))) {
+ if (!SUCCEEDED (SHGetFolderPathA (NULL, CSIDL_LOCAL_APPDATA, NULL, 0, (char *)buffer))) {
FcConfigMessage (parse, FcSevereError, "SHGetFolderPathA failed");
return NULL;
}
strcat ((char *)path, "\\Microsoft\\Windows\\Fonts");
} else if (strcmp ((const char *)path, "WINDOWSFONTDIR") == 0) {
- int rc;
- path = buffer;
- _ensureWin32GettersReady();
- rc = pGetSystemWindowsDirectory ((LPSTR)buffer, sizeof (buffer) - 20);
- if (rc == 0 || rc > sizeof (buffer) - 20) {
+ wchar_t *wpath;
+ int size_needed;
+
+ if (!SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Fonts, 0, NULL, &wpath))) {
- FcConfigMessage (parse, FcSevereError, "GetSystemWindowsDirectory failed");
+ FcConfigMessage (parse, FcSevereError, "SHGetKnownFolderPath for FOLDERID_Fonts failed");
return NULL;
}
- if (path[strlen ((const char *)path) - 1] != '\\')
- strcat ((char *)path, "\\");
- strcat ((char *)path, "fonts");
+ /* We assume that the code using fontconfig handles UTF-8 strings and not system codepage */
+ size_needed = WideCharToMultiByte (CP_UTF8, 0, wpath, wcslen(wpath), NULL, 0, NULL, NULL);
+ if (size_needed <= 0 || size_needed > sizeof(buffer)) {
+ FcConfigMessage (parse, FcSevereError, "WideCharToMultiByte failed");
+ CoTaskMemFree (wpath);
+ return NULL;
+ }
+ path = buffer;
+ WideCharToMultiByte(CP_UTF8, 0, wpath, wcslen(wpath), path, size_needed, NULL, NULL);
+ CoTaskMemFree (wpath);
+ } else if (strcmp ((const char *)path, "WINDOWSAPPXFONTDIRS") == 0) {
+ char *appxFontDirs = appx_fonts_get_dirs();
+ char *semicolon;
+ char *p = appxFontDirs;
+ e = FcStrSetCreate ();
+ while ((semicolon = strchr(p, ';')) != NULL) {
+ *semicolon = '\0';
+ FcStrSetAdd (e, (FcChar8*)p);
+ p = semicolon + 1;
+ }
+ free(appxFontDirs);
+ path = NULL;
+ dontInsertRetval = FcTrue;
} else {
if (!prefix) {
if (!FcStrIsAbsoluteFilename (path) && path[0] != '~')
@@ -1388,7 +1388,7 @@
e->strs[i] = s;
}
}
- if (!FcStrSetInsert (e, retval, 0)) {
+ if (!dontInsertRetval && !FcStrSetInsert (e, retval, 0)) {
FcStrSetDestroy (e);
e = NULL;
}
@@ -2288,7 +2288,7 @@
char szFPath[MAX_PATH + 1];
size_t len;
- if (!(pSHGetFolderPathA && SUCCEEDED (pSHGetFolderPathA (NULL, /* CSIDL_LOCAL_APPDATA */ 28, NULL, 0, szFPath)))) {
+ if (!SUCCEEDED (SHGetFolderPathA (NULL, CSIDL_LOCAL_APPDATA, NULL, 0, szFPath))) {
FcConfigMessage (parse, FcSevereError, "SHGetFolderPathA failed");
goto bail;
}
@@ -3169,7 +3169,9 @@
name);
ret = FcFalse;
goto bail0;
- }
+ } else
+ fprintf (stderr, "Fontconfig: Successfully opened directory %s\n", dir);
+
/* freed below */
file = (FcChar8 *)malloc (strlen ((char *)dir) + 1 + FC_MAX_FILE_LEN + 1);
if (!file) {
@@ -3363,9 +3363,6 @@
FcStrBuf reason;
FcStrBufInit (&reason, NULL, 0);
-#ifdef _WIN32
- _ensureWin32GettersReady();
-#endif
filename = FcConfigGetFilename (config, name);
if (!filename) {
@@ -3407,7 +3407,8 @@
FcStrBufString (&reason, (FcChar8 *)"Unable to open ");
FcStrBufString (&reason, realfilename);
goto bail1;
- }
+ } else
+ fprintf (stderr, "Fontconfig: Successfully opened file %s\n", realfilename);
do {
len = read (fd, buf, BUFSIZ);
@@ -3488,23 +3488,6 @@
return FcConfigParseAndLoadFromMemoryInternal (config, (const FcChar8 *)"memory", buffer, complain, FcTrue);
}
-#ifdef _WIN32
-static void
-_ensureWin32GettersReady ()
-{
- if (!pGetSystemWindowsDirectory) {
- HMODULE hk32 = GetModuleHandleA ("kernel32.dll");
- if (!(pGetSystemWindowsDirectory = (pfnGetSystemWindowsDirectory)GetProcAddress (hk32, "GetSystemWindowsDirectoryA")))
- pGetSystemWindowsDirectory = (pfnGetSystemWindowsDirectory)GetWindowsDirectory;
- }
- if (!pSHGetFolderPathA) {
- HMODULE hSh = LoadLibraryA ("shfolder.dll");
- /* the check is done later, because there is no provided fallback */
- if (hSh)
- pSHGetFolderPathA = (pfnSHGetFolderPathA)GetProcAddress (hSh, "SHGetFolderPathA");
- }
-}
-#endif // _WIN32
#define __fcxml__
#include "fcaliastail.h"
--- src/fcdwrite.cxx
+++ src/fcdwrite.cxx
@@ -0,0 +1,127 @@
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0A00
+
+#include <windows.h>
+#include <dwrite_3.h>
+#include <wrl/client.h>
+
+#include <cstring>
+#include <iostream>
+#include <set>
+#include <string>
+#include <vector>
+
+using Microsoft::WRL::ComPtr;
+
+static std::string wide_string_to_string(const std::wstring& wide_string)
+{
+ if (wide_string.empty()) {
+ return "";
+ }
+
+ const auto size_needed = WideCharToMultiByte(CP_UTF8, 0, wide_string.data(), (int)wide_string.size(), nullptr, 0, nullptr, nullptr);
+ if (size_needed <= 0) {
+ std::wcerr << L"WideCharToMultiByte() failed: " + std::to_wstring(size_needed) << std::endl;
+ return "";
+ }
+
+ std::string result(size_needed, 0);
+ WideCharToMultiByte(CP_UTF8, 0, wide_string.data(), (int)wide_string.size(), result.data(), size_needed, nullptr, nullptr);
+ return result;
+}
+
+extern "C" char* appx_fonts_get_dirs()
+{
+ // We assume COM has been initialised by the surrounding app
+ HRESULT hr;
+ ComPtr<IDWriteFactory7> factory;
+
+ hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,
+ __uuidof(IDWriteFactory7),
+ reinterpret_cast<IUnknown**>(factory.GetAddressOf()));
+
+ if (FAILED(hr)) {
+ std::wcerr << L"Failed to create DirectWrite factory\n";
+ return nullptr;
+ }
+
+ // Get the unified system font set (covers system, user, Store fonts)
+ ComPtr<IDWriteFontSet2> fontSet;
+ hr = factory->GetSystemFontSet(FALSE, &fontSet);
+ if (FAILED(hr)) {
+ std::wcerr << L"GetSystemFontSet failed\n";
+ return nullptr;
+ }
+
+ ComPtr<IDWriteFontSet3> fontSet3;
+ hr = fontSet.As(&fontSet3);
+ if (FAILED(hr)) {
+ std::wcerr << L"Could not get IDWriteFontSet3 from IDWriteFontSet2\n";
+ return nullptr;
+ }
+
+ std::set<std::wstring> folders;
+ UINT32 count = fontSet->GetFontCount();
+
+ for (UINT32 i = 0; i < count; ++i) {
+ auto sourceType = fontSet3->GetFontSourceType(i);
+
+ // We are interested only in the fonts from the Store, i.e. APPX ones, here.
+ if (sourceType != DWRITE_FONT_SOURCE_TYPE_APPX_PACKAGE)
+ continue;
+
+ ComPtr<IDWriteFontFaceReference> faceRef;
+ if (FAILED(fontSet->GetFontFaceReference(i, &faceRef)))
+ continue;
+
+ // Resolve to actual font face to access files
+ ComPtr<IDWriteFontFace3> face;
+ if (FAILED(faceRef->CreateFontFace(&face)))
+ continue;
+
+ UINT32 fileCount = 0;
+ face->GetFiles(&fileCount, nullptr);
+
+ std::vector<ComPtr<IDWriteFontFile>> files(fileCount);
+ face->GetFiles(&fileCount, reinterpret_cast<IDWriteFontFile**>(files.data()));
+
+ for (UINT32 f = 0; f < fileCount; ++f) {
+ const void* refKey = nullptr;
+ UINT32 refKeySize = 0;
+
+ files[f]->GetReferenceKey(&refKey, &refKeySize);
+
+ ComPtr<IDWriteFontFileLoader> loader;
+ files[f]->GetLoader(&loader);
+
+ ComPtr<IDWriteLocalFontFileLoader> localLoader;
+ if (SUCCEEDED(loader.As(&localLoader))) {
+ UINT32 pathLen = 0;
+ hr = localLoader->GetFilePathLengthFromKey(refKey, refKeySize, &pathLen);
+ if (SUCCEEDED(hr)) {
+ std::wstring path(pathLen + 1, L'\0');
+ hr = localLoader->GetFilePathFromKey(refKey, refKeySize, path.data(), pathLen + 1);
+
+ // We actually only want the folders that such
+ // fonts are in. Fontconfig will, stupidly enough,
+ // scan the whole folder looking for font files,
+ // even if we here would already know the font
+ // files. But I can't be arsed to re-wire
+ // fontconfig any more than absolutely necessary,
+ // it is complicated enough as is.
+
+ if (SUCCEEDED(hr)) {
+ auto lastBackslash = path.find_last_of(L'\\');
+ if (lastBackslash != std::wstring::npos)
+ folders.insert(path.substr(0, lastBackslash));
+ }
+ }
+ }
+ }
+ }
+
+ std::string result;
+ for (const auto& i : folders)
+ result += wide_string_to_string(i) + ";";
+ return _strdup(result.c_str());
+}