Page MenuHome

UI: Add User Fonts to System List for Windows
Needs ReviewPublic

Authored by Yuki Hashimoto (hzuika) on Jul 23 2021, 6:28 PM.
This revision needs review, but there are no reviewers specified.

Details

Reviewers
None
Summary

This patch adds user fonts folder (%LOCALAPPDATA%\Microsoft\Windows\Fonts\) to system list for windows.

Problem

When windows users install fonts, there are two Fonts folders.

  • Install for all users
    • C:\Windows\Fonts (called System Fonts Folder)
  • Install (current user only)
    • C:\Users\[username]\AppData\Local\Microsoft\Windows\Fonts (called Users Fonts Folder)

However, blender adds only System Fonts Folder (Fonts in blender) in the system list.
In addition, Users Fonts Folder is in hidden folder (AppData), so most of users may not find the folder.

Diff Detail

Repository
rB Blender
Branch
usersfonts (branched from master)
Build Status
Buildable 15995
Build 15995: arc lint + arc unit

Event Timeline

Yuki Hashimoto (hzuika) requested review of this revision.Jul 23 2021, 6:28 PM
Yuki Hashimoto (hzuika) created this revision.

T75028 mentioned a bit about this.

Screenshot.
I call the folder name UsersFonts for now.

This is a cool idea, but currently wondering if there is another way to do this nicely.

If we know we are viewing a folder that matches FOLDERID_Fonts (usually your c:\windows\fonts folder), then we could iterate through the items in the registry at HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Fonts which shows all the full paths of each font installed outside that folder (the ones usually in your :\Users\[username]\AppData\Local\Microsoft\Windows\Fonts folder).

For each of these would could just add an entry to the file browser directory listing, using font display name as "name", full path in redirection_path, and give it attribute FILE_ATTR_ALIAS. That way the folder would just show the contents of both folders in the same way it does when viewing in Windows Explorer.

If we know we are viewing a folder that matches FOLDERID_Fonts (usually your c:\windows\fonts folder), then we could iterate through the items in the registry at HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Fonts which shows all the full paths of each font installed outside that folder (the ones usually in your :\Users\[username]\AppData\Local\Microsoft\Windows\Fonts folder).

For each of these would could just add an entry to the file browser directory listing, using font display name as "name", full path in redirection_path, and give it attribute FILE_ATTR_ALIAS. That way the folder would just show the contents of both folders in the same way it does when viewing in Windows Explorer.

That's a very good idea. Thank you for the information!
I will investigate registry operations.

@Yuki Hashimoto (hzuika) - I was SO wrong...

I have two fonts in that C:\Users\[username]\AppData\Local\Microsoft\Windows\Fonts folder and they are NOT shown to me when I browse C:\Windows\Fonts. I was sure that was the behavior. Oh well, at least I now have reference code that adds extra items to folders now. LOL.

So your patch idea seems nice. Although I would only add this second icon to the list if it actually contains something. You could get a quick count of that directory's contents. Although I think on Windows you can reliably use the stat size like this:

BLI_stat_t stats;
BLI_stat(folderpath, &stats);
if (stats.st_size > 0) ...

There might also be ways of simplifying your patch a bit. Rather than duplicating most of the code in fsmenu_add_windows_folder you could add a new function that both it and yours could then use, like this:

#ifdef WIN32
/* Get a known Windows folder path. */
static void get_windows_folder(REFKNOWNFOLDERID rfid, char *path, size_t len)
{
  LPWSTR pPath32;
  if (SHGetKnownFolderPath(rfid, 0, NULL, &pPath32) == S_OK) {
    BLI_strncpy_wchar_as_utf8(path, pPath32, FILE_MAXDIR);
    CoTaskMemFree(pPath32);
  }
}

/* Add a Windows known folder path to the System list. */
static void fsmenu_add_windows_folder(struct FSMenu *fsmenu,
                                      FSMenuCategory category,
                                      REFKNOWNFOLDERID rfid,
                                      const char *name,
                                      const int icon,
                                      FSMenuInsert flag)
{
  char path[FILE_MAXDIR] = {0};
  get_windows_folder(rfid, path, sizeof(path));
  if (path) {
    fsmenu_insert_entry(fsmenu, category, path, name, icon, flag);
  }
}
#endif

Hi @Harley Acheson (harley) . Thank you for your comment.

@Yuki Hashimoto (hzuika) - I was SO wrong...

Perhaps, you were not wrong.

I will try your suggested way to get the font name and path from the registry rather than improving my patch. I thought it would be more user-friendly to display the font names in a single folder, similar to the Windows File Explorer.

In addition, there was one problem with my patch.
Windows allows fonts to be installed using a shortcut (advanced option). In this case, the font path is the location where the user installed the font. This path is neither C:\Windows\Fonts nor C:\Users\[username]\AppData\Local\Microsoft\Windows\Fonts. However, Explorer will show the installed font name in the C:\Windows\Font folder.

This problem can be solved by using the registry. The installed font using a shortcut can be found at HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Fonts. This registry path also contains other fonts that are installed on the system. The user-installed fonts are in HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Fonts, as you say.

I'm trying to get the font information from these two registry paths when Blender's file browser displays C:\Windows\Fonts. Specifically, I plan to put the process in the filelist_readjob_list_dir function or filelist_readjob_do function in filelist.c.

This work is taking some time and is not yet complete.

Yuki Hashimoto (hzuika) retitled this revision from UI: Add Users Fonts to System List for Windows to [WIP] UI: Add Users Fonts to System List for Windows.Jul 31 2021, 5:29 AM
Yuki Hashimoto (hzuika) edited the summary of this revision. (Show Details)

@Yuki Hashimoto (hzuika) - I will try your suggested way to get the font name and path from the registry rather than improving my patch

Although I didn't like my approach, there could be something useful in it for you. The following compares the current directory against FOLDERID_Fonts, and if the same reads those fonts from the registry and adds them as "fake" items to the list, each item having a full path to the font. It works, but not tested much. I find it more trouble and complexity than I'd like for such a feature. And I'd prefer if the fonts path (FOLDERID_Fonts) were cached somewhere, but I didn't get past this proof of concept phase. Have fun.

@Harley Acheson (harley) Thank you so much!

I find it more trouble and complexity than I'd like for such a feature. And I'd prefer if the fonts path (FOLDERID_Fonts) were cached somewhere, but I didn't get past this proof of concept phase.

You're right, using the registry can be complicated.
If there is a process to add the userfonts folder to the system bookmarks (my patch) and a option to display the font names instead of the font files, it may be possible to avoid the registry.

@Yuki Hashimoto (hzuika) -…option to display the font names instead of the font files, it may be possible to avoid the registry.

I’m not quite sure what you mean about differentiating between font names and font files. Was this about shortcuts to user files or something else? If about the how the font file names do not match the better descriptive names shown in Windows explorer, note that changed in Blender a couple of days ago: https://developer.blender.org/D12020

If about the how the font file names do not match the better descriptive names shown in Windows explorer, note that changed in Blender a couple of days ago: https://developer.blender.org/D12020

Sorry @Harley Acheson (harley), I didn't notice this commit.
I was too busy to build Blender and check new feature for the last week.

This commit solved what I was talking about.
So, I will improve this patch to add a user font folder as originally planned.

Yuki Hashimoto (hzuika) updated this revision to Diff 40190.EditedAug 1 2021, 11:19 AM
Yuki Hashimoto (hzuika) retitled this revision from [WIP] UI: Add Users Fonts to System List for Windows to UI: Add User Fonts to System List for Windows.
Yuki Hashimoto (hzuika) edited the summary of this revision. (Show Details)

Update diff

  • add new function (get_windows_folder)
  • add checking folder size
  • change bookmark name from "UsersFonts" to "User Fonts"

Update diff.

  • Use BLI_path_append instead of BLI_snprintf

I did some testing, but I'm not sure if stat is working correctly.

  1. Even after deleting all the fonts in the User fonts directory, st_size returned 4096.
  2. After that, deleting the User fonts directory and creating it again returned 0 for st_size.
  3. When I installed the fonts in this new User fonts directory, st_size remained at 0.

I couldn't figure out when st_size is updated.

source/blender/editors/space_file/fsmenu.c
653–654

It needs to be investigated if st_size shows the correct value.

  • I don't know if this is a better change, but instead of a new function, I added a process in fsmenu_add_windows_folder.
  • I wasn't sure if st_size was correct, so I changed it to check if user fonts folder existed.

@Yuki Hashimoto (hzuika) - I did some testing, but I'm not sure if stat is working correctly.

Oh, sorry if I steered you wrong there; I didn’t test that stat size idea much. Since you are in a Windows-only section of code with access to API calls you could use one of these?

https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathisdirectoryemptyw

Since you are in a Windows-only section of code with access to API calls you could use one of these?
https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathisdirectoryemptyw

Thank you for the information. I'll try it.

Update diff

  • use Windows API (PathIsDirectoryEmpty) instead of BLI_stat