Skip to content

Commit

Permalink
Rework Windows font collector (#107)
Browse files Browse the repository at this point in the history
[src\meson.build] Add DirectWrite has dependency

[src\font_file_lister_gdi] Rework GDI FontCollector to use DirectWrite

This replaces all the logic of using the Windows registry to obtain the font path by using DirectWrite. The goal is simply to improve the quality of the code. This doesn't change any functionality

[src\meson.build] Remove Uniscribe has dependency

Uniscribe was only used for the FontCollector. Since we now use DirectWrite, we don't need it anymore.

[src\dialog_fonts_collector] Catch exceptions that FontCollector may raise

On Windows, the initialization of the FontCollector can raise an exception

[src\font_file_lister] Document the exception that GdiFontFileLister can throw

[src\font_file_lister_gdi] Correct possible memory leak when an error occur

Fix error caused by AddFontResource on Windows 10 or higher

[meson.build] Replace add_project_arguments with conf.set for HAVE_DWRITE_3

[src\dialog_fonts_collector] Update message error and optimisation

[src\font_file_lister_gdi] Correct documentation typo

[src\font_file_lister_gdi] Cosmetic nit - Initialize hfont in one line

[src\font_file_lister_gdi] Cosmetic nit - Remove if statements brace

[src\font_file_lister_gdi] Replace WCHAR param of normalizeFilePathCase to std::wstring

[src\font_file_lister_gdi] Replace WCHAR by std::wstring

[src\font_file_lister_gdi] Use IDWriteFontFace::GetSimulations to detect fake_italic/fake_bold

See this comment: #107 (comment)

[src\font_file_lister_gdi] If Win7/8 has Win 10 SDK on compile time, correctly verify if font has character(s)

With the Visual Studio 2019 toolchain on Windows 7, it installs the Windows 10 SDK by default. Because of this, ``HAVE_DWRITE_3`` is true, so the ``QueryInterface`` always fails. Now, if the ``QueryInterface`` fails, we try to verify if the font has characters with a Windows Vista SP2 compatible code.

[src\font_file_lister_gdi] Support facename that contains only whitespace AND truncated facename

Problem 1:
Previously, if a user wrote "\fn   ", it would return the font Arial, which is not what we want. This is because when we request EnumFontFamiliesEx with whitespace or an empty lfFaceName, it will enumerate all the installed fonts.

Solution 1:
To resolve this issue, let's implement a solution similar to libass to determine if the selected facename exists: https://github.com/libass/libass/blob/649a7c2e1fc6f4188ea1a89968560715800b883d/libass/ass_directwrite.c#L737-L747

Problem 2:
GDI truncates font names to 31 characters. See: libass/libass#459
However, since I changed the method to determine if a facename exists, I ensured that we still support this "feature".

To test this, I used the font in: libass/libass#710

[src\font_file_lister_gdi] Add a FIXME comment regarding the utilization of std::wstring over WCHAR

[src\font_file_lister_gdi] Add FIXME comment about charset
  • Loading branch information
moi15moi authored and arch1t3cht committed Mar 20, 2024
1 parent 897ca32 commit 7543060
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 265 deletions.
4 changes: 4 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ if host_machine.system() == 'windows' and not get_option('directsound').disabled
endif
endif

if host_machine.system() == 'windows' and cc.has_header('dwrite_3.h')
conf.set('HAVE_DWRITE_3', 1)
endif

if host_machine.system() == 'darwin'
frameworks_dep = dependency('appleframeworks', modules : ['CoreText', 'CoreFoundation', 'AppKit', 'Carbon', 'IOKit'])
deps += frameworks_dep
Expand Down
9 changes: 8 additions & 1 deletion src/dialog_fonts_collector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,14 @@ void FontsCollectorThread(AssFile *subs, agi::fs::path const& destination, FcMod
collector->AddPendingEvent(ValueEvent<color_str_pair>(EVT_ADD_TEXT, -1, {colour, text.Clone()}));
};

auto paths = FontCollector(AppendText).GetFontPaths(subs);
std::vector<agi::fs::path> paths;
try {
paths = FontCollector(AppendText).GetFontPaths(subs);
}
catch (agi::EnvironmentError const& err) {
AppendText(fmt_tl("* An error occurred when enumerating the used fonts: %s.\n", err.GetMessage()), 2);
}

if (paths.empty()) {
collector->AddPendingEvent(wxThreadEvent(EVT_COLLECTION_DONE));
return;
Expand Down
14 changes: 7 additions & 7 deletions src/font_file_lister.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ struct CollectionResult {
};

#ifdef _WIN32
#include <dwrite.h>
class GdiFontFileLister {
std::unordered_multimap<uint32_t, agi::fs::path> index;
agi::scoped_holder<HDC> dc;
std::string buffer;

bool ProcessLogFont(LOGFONTW const& expected, LOGFONTW const& actual, std::vector<int> const& characters);
agi::scoped_holder<HDC> dc_sh;
agi::scoped_holder<IDWriteFactory*> dwrite_factory_sh;
agi::scoped_holder<IDWriteFontCollection*> font_collection_sh;
agi::scoped_holder<IDWriteGdiInterop*> gdi_interop_sh;

public:
/// Constructor
/// @param cb Callback for status logging
GdiFontFileLister(FontCollectorStatusCallback &cb);
/// @throws agi::EnvironmentError if an error occurs during construction.
GdiFontFileLister(FontCollectorStatusCallback &);

/// @brief Get the path to the font with the given styles
/// @param facename Name of font face
Expand Down
Loading

0 comments on commit 7543060

Please sign in to comment.