microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.32k stars 676 forks source link

Unpackaged Win32 app using WinUI3 Xaml Island - problems with external fonts referenced in xaml #10054

Open PetrMinar opened 2 hours ago

PetrMinar commented 2 hours ago

Describe the bug

<TextBlock Text="Test" FontFamily="file:///c:/font.ttf#font" /> does not work.

It is probably a very old problem that no one tried to fix or find a workaround. It was reported almost five years ago...

WinUI3 Xaml does not support file: as part of Uri. It can be seen in ResourceManager::TryGetLocalResource where only ms-appdata:, ms-appx: and ms-resource: are handled.

That is understandable for UWP applicarions and Packaged Desktop applications (although when they have broadFileSystemAccess it should work too, right?). But it should work for Unpackaged Deskop application.

While searching for workaround I found that

ms-appx:///xxx is essentialy transformed to ms-resource:///files/xxx. ms-resource:///files/xxx uses m_pFallbackResourceProvider and combines xxxwith m_pBaseUri. m_pBaseUri is initialized to GetModuleFileName(NULL, applicationDirectory, MAX_PATH) in CommonResourceProvider::Create.

This workaround looks like implementation detail and might stop working in future. And of course is limited to exe directory only. What else should we use?

Steps to reproduce the bug

<TextBlock Text="Test" FontFamily="file:///c:/font.ttf#font" /> does not work.

Expected behavior

<TextBlock Text="Test" FontFamily="file:///c:/font.ttf#font" /> Should work.

Screenshots

No response

NuGet package version

WinUI 3 - Windows App SDK 1.6.1: 1.6.240923002

Windows version

Windows 11 (22H2): Build 22621

Additional context

No response

DarranRowe commented 2 hours ago

Well, there are two things to note about this. First, it is the component itself that determines if it supports a URI scheme. I would also imagine, in this case, it would be the component that determines how it is referenced. Second, WinUI 3 itself depends on ms-appx being interpreted as the current application's executable directory or .pri file when unpackaged.

Two examples of this:

//Extract from App.xaml.g.h, generated by the Xaml compiler.

void InitializeComponent()
{
    if (_contentLoaded)
        return;

   _contentLoaded = true;

    ::winrt::Windows::Foundation::Uri resourceLocator{ L"ms-appx:///App.xaml" };
    ::winrt::Microsoft::UI::Xaml::Application::LoadComponent(*this, resourceLocator);
}
//Extract from MainWindow.xaml.g.hpp, generated by the Xaml compiler.

template <typename D, typename ... I>
void MainWindowT<D, I...>::InitializeComponent()
{
    if (!_contentLoaded)
    {
        _contentLoaded = true;
        ::winrt::Windows::Foundation::Uri resourceLocator{ L"ms-appx:///MainWindow.xaml" };
        ::winrt::Microsoft::UI::Xaml::Application::LoadComponent(*this, resourceLocator, ComponentResourceLocation::Application);
    }
}

It is therefore tough to believe that Microsoft.UI.Xaml.Application.LoadComponent has been updated to work with unpackaged applications, but no other WinUI 3 component wouldn't have been updated for unpackaged application support. But yes, this behaviour really should be documented.

PetrMinar commented 58 minutes ago

@DarranRowe Thank you for your response

First, it is the component itself that determines if it supports a URI scheme. In this case the component is Microsoft::UI::Xaml::Media::FontFamily that uses builtin ResourceManager (probably shared by whole Microsoft-UI-Xaml framework). ResourceManager only supports the three mentioned Uri formats (can be seen in ResourceManager::TryGetLocalResource).

Second, WinUI 3 itself depends on ms-appx being interpreted as the current application's executable directory Hmm that is more interesting... the code itself is in App.xaml.g.h / MainWindow.xaml.g.hpp and is generated during project build. So it "hard-wired" in all applications using WinUI3 and therefore it cannot be changed in future unless all existing apps are broken. Now I feel relieved to use it in production code, because that wouldn't happen... or would it?

But yes, this behaviour really should be documented. Yes, I agree.

All this is only part of the problem. What if I want to target a file outside the exe directory? Should I copy it there? That sounds completly wrong...