From 8bba31e05193f57de5817c2976ae9c8bbd31e359 Mon Sep 17 00:00:00 2001 From: moi15moi Date: Sat, 14 Sep 2024 12:29:55 -0400 Subject: [PATCH] On Windows, wxFontEnumerator::GetFacenames and wxFont::GetFaceName use GDI, which truncates the face name to 31 characters. Because of this, some users like in libass/libass#710 may think they have retrieved the correct face name, but that is not the case. It use the same method has GetMSWFaceName to get the family name: https://github.com/wxWidgets/wxWidgets/blob/44068b5550bc82ac535e82680d158d3e03d7aa5f/src/msw/font.cpp#L269-L303 --- src/command/edit.cpp | 5 +- src/dialog_style_manager.cpp | 5 +- src/font.cpp | 97 ++++++++++++++++++++++++++++++++++++ src/font.h | 31 ++++++++++++ src/meson.build | 1 + src/subs_edit_box.cpp | 4 +- 6 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 src/font.cpp create mode 100644 src/font.h diff --git a/src/command/edit.cpp b/src/command/edit.cpp index e99de01673..fc603d88ce 100644 --- a/src/command/edit.cpp +++ b/src/command/edit.cpp @@ -38,6 +38,7 @@ #include "../compat.h" #include "../dialog_search_replace.h" #include "../dialogs.h" +#include "../font.h" #include "../format.h" #include "../include/aegisub/context.h" #include "../initial_line_state.h" @@ -519,8 +520,8 @@ struct edit_font final : public Command { shift += parsed.set_tag(tag_name, value, norm_sel_start, sel_start + shift); }; - if (font.GetFaceName() != startfont.GetFaceName()) - do_set_tag("\\fn", from_wx(font.GetFaceName())); + if (GetFaceName(font) != GetFaceName(startfont)) + do_set_tag("\\fn", from_wx(GetFaceName(font))); if (font.GetPointSize() != startfont.GetPointSize()) do_set_tag("\\fs", std::to_string(font.GetPointSize())); if (font.GetWeight() != startfont.GetWeight()) diff --git a/src/dialog_style_manager.cpp b/src/dialog_style_manager.cpp index 706ea7146e..99b49b83f7 100644 --- a/src/dialog_style_manager.cpp +++ b/src/dialog_style_manager.cpp @@ -37,6 +37,7 @@ #include "dialog_style_editor.h" #include "dialogs.h" #include "format.h" +#include "font.h" #include "help_button.h" #include "include/aegisub/context.h" #include "libresrc/libresrc.h" @@ -262,9 +263,7 @@ DialogStyleManager::DialogStyleManager(agi::Context *context) , commit_connection(c->ass->AddCommitListener(&DialogStyleManager::LoadCurrentStyles, this)) , active_line_connection(c->selectionController->AddActiveLineListener(&DialogStyleManager::OnActiveLineChanged, this)) , font_list(std::async(std::launch::async, []() -> wxArrayString { - wxArrayString fontList = wxFontEnumerator::GetFacenames(); - fontList.Sort(); - return fontList; + return GetFaceNames(); })) { using std::bind; diff --git a/src/font.cpp b/src/font.cpp new file mode 100644 index 0000000000..56df27282a --- /dev/null +++ b/src/font.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2025, arch1t3cht +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +/// @file font.cpp +/// @brief font face name provider +/// + + +#include "font.h" + +#include + +#ifdef _WIN32 +#include +#include + +#include +#include +#endif + + +wxArrayString GetFaceNames() { +#ifdef _WIN32 + + wxArrayString truncated_face_name_list = wxFontEnumerator::GetFacenames(); + wxArrayString face_name_list = wxArrayString(); + face_name_list.Alloc(truncated_face_name_list.GetCount()); + + for (const wxString& face_name: truncated_face_name_list) { + wxFont font = wxFont( + 10, // Any value would be good + wxFONTFAMILY_DEFAULT, + wxFONTSTYLE_NORMAL, + wxFONTWEIGHT_NORMAL, + false, + face_name + ); + + face_name_list.Add(GetFaceName(font)); + + } + + face_name_list.Sort(); + return face_name_list; + +#else + + wxArrayString font_list = wxFontEnumerator::GetFacenames(); + font_list.Sort(); + return font_list; + +#endif + +} + +wxString GetFaceName(const wxFont& font) { +#ifdef _WIN32 + + HDC dc = CreateCompatibleDC(nullptr); + if (dc == nullptr) + throw agi::EnvironmentError("Failed to initialize the HDC"); + agi::scoped_holder dc_sh(dc, [](HDC dc) { DeleteDC(dc); }); +; + WXHFONT hfont = font.GetHFONT(); + SelectFont(dc_sh, hfont); + + UINT otm_size = GetOutlineTextMetricsW(dc_sh, 0, nullptr); + if (!otm_size) + throw agi::EnvironmentError("Failed to initialize the otm_size"); + + OUTLINETEXTMETRICW* otm = reinterpret_cast(malloc(otm_size)); + agi::scoped_holder otm_sh(otm, [](OUTLINETEXTMETRICW* otm) { free(otm); }); + + otm->otmSize = otm_size; + if (!GetOutlineTextMetricsW(dc_sh, otm_size, otm_sh)) + throw agi::EnvironmentError("Failed to initialize the otm"); + + return reinterpret_cast(otm) + wxPtrToUInt(otm->otmpFamilyName)/sizeof(wxChar); +#else + + return font.GetFaceName(); + +#endif +} diff --git a/src/font.h b/src/font.h new file mode 100644 index 0000000000..e7930c3668 --- /dev/null +++ b/src/font.h @@ -0,0 +1,31 @@ +// Copyright (c) 2025, arch1t3cht +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// Aegisub Project http://www.aegisub.org/ + +/// @file font.h +/// @see font.cpp +/// + +#include +#include + +/// @brief Get all the font face name installed in the system. +/// @return A list containing all the font face name sorted +wxArrayString GetFaceNames(); + +/// @brief Get the font face name. +/// @param font The path to be normalized. It can be a directory or a file. +/// @return The font face name. +wxString GetFaceName(const wxFont& font); diff --git a/src/meson.build b/src/meson.build index a9ad845013..174ad6d597 100644 --- a/src/meson.build +++ b/src/meson.build @@ -91,6 +91,7 @@ aegisub_src = files( 'fft.cpp', 'fold_controller.cpp', 'font_file_lister.cpp', + 'font.cpp', 'frame_main.cpp', 'gl_text.cpp', 'gl_wrap.cpp', diff --git a/src/subs_edit_box.cpp b/src/subs_edit_box.cpp index ad29f4e722..5e0f1172ff 100644 --- a/src/subs_edit_box.cpp +++ b/src/subs_edit_box.cpp @@ -41,6 +41,7 @@ #include "compat.h" #include "dialog_style_editor.h" #include "flyweight_hash.h" +#include "font.h" #include "include/aegisub/context.h" #include "include/aegisub/hotkey.h" #include "initial_line_state.h" @@ -128,8 +129,7 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context) style_edit_button = new wxButton(this, -1, _("Edit"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); style_edit_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { if (active_style) { - wxArrayString font_list = wxFontEnumerator::GetFacenames(); - font_list.Sort(); + wxArrayString font_list = GetFaceNames(); DialogStyleEditor(this, active_style, c, nullptr, "", font_list).ShowModal(); } });