Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Font Install #5042

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Prev Previous commit
Next Next commit
initial implementaiton for font installer
ryfu-msft committed Dec 3, 2024
commit 42e53bfbd28ba25719517879f3393dff30b247c3
35 changes: 30 additions & 5 deletions src/AppInstallerCLICore/FontInstaller.cpp
Original file line number Diff line number Diff line change
@@ -11,15 +11,40 @@

namespace AppInstaller::CLI::Font
{
FontInstaller::FontInstaller(const std::string& familyName, Manifest::ScopeEnum scope)
namespace
{
m_scope = scope;
m_familyName = familyName;
constexpr std::wstring_view s_FontsPathSubkey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
}


// Get the expected state from the family name and scope.
FontInstaller::FontInstaller(Manifest::ScopeEnum scope) : m_scope(scope)
{
if (scope == Manifest::ScopeEnum::Machine)
{
m_installLocation = Runtime::GetPathTo(Runtime::PathName::FontsMachineInstallLocation);
m_key = Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, std::wstring{ s_FontsPathSubkey });
}
else
{
m_installLocation = Runtime::GetPathTo(Runtime::PathName::FontsUserInstallLocation);
m_key = Registry::Key::OpenIfExists(HKEY_CURRENT_USER, std::wstring{ s_FontsPathSubkey });
}
}

void FontInstaller::Install()
void FontInstaller::Install(const std::map<std::wstring, std::filesystem::path> fontFiles)
{
// TODO: Get all font files and check if font file already exists.
for (const auto& [fontName, fontFilePath] : fontFiles)
{
std::filesystem::path fileName = fontFilePath.filename();
std::filesystem::path fontFileInstallationPath = m_installLocation / fileName;

AICLI_LOG(CLI, Info, << "Moving font file to: " << fontFileInstallationPath.u8string());
AppInstaller::Filesystem::RenameFile(fontFilePath, fontFileInstallationPath);

// TODO: Need to fix access permission for writing to the registry.
AICLI_LOG(CLI, Info, << "Creating font subkey for: " << AppInstaller::Utility::ConvertToUTF8(fontName));
m_key.SetValue(fontName, fontFileInstallationPath, REG_SZ);
}
}
}
10 changes: 5 additions & 5 deletions src/AppInstallerCLICore/FontInstaller.h
Original file line number Diff line number Diff line change
@@ -5,17 +5,17 @@

namespace AppInstaller::CLI::Font
{
// Object representation of the metadata and functionality required for installing a font package.
struct FontInstaller
{
std::filesystem::path FontFileLocation;
FontInstaller(Manifest::ScopeEnum scope);

FontInstaller(const std::string& familyName, Manifest::ScopeEnum scope);
std::filesystem::path FontFileLocation;

void Install();
void Install(const std::map<std::wstring, std::filesystem::path> fontFiles);

private:
Manifest::ScopeEnum m_scope;
std::string m_familyName;
std::filesystem::path m_installLocation;
Registry::Key m_key;
};
}
2 changes: 2 additions & 0 deletions src/AppInstallerCLICore/Workflows/DownloadFlow.cpp
Original file line number Diff line number Diff line change
@@ -57,6 +57,8 @@ namespace AppInstaller::CLI::Workflow
return L".msix"sv;
case InstallerTypeEnum::Zip:
return L".zip"sv;
case InstallerTypeEnum::Font:
return L".ttf"sv;
default:
THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
}
36 changes: 36 additions & 0 deletions src/AppInstallerCLICore/Workflows/FontFlow.cpp
Original file line number Diff line number Diff line change
@@ -5,10 +5,12 @@
#include "TableOutput.h"
#include <winget/Fonts.h>
#include <AppInstallerRuntime.h>
#include <FontInstaller.h>

namespace AppInstaller::CLI::Workflow
{
using namespace AppInstaller::CLI::Execution;
using namespace AppInstaller::CLI::Font;

namespace
{
@@ -124,5 +126,39 @@ namespace AppInstaller::CLI::Workflow

void FontInstallImpl(Execution::Context& context)
{
Manifest::ScopeEnum scope = AppInstaller::Manifest::ConvertToScopeEnum(context.Args.GetArg(Execution::Args::Type::InstallScope));
FontInstaller fontInstaller = FontInstaller(scope);
std::filesystem::path& installerPath = context.Get<Execution::Data::InstallerPath>();
std::map<std::wstring, std::filesystem::path> fontFileMap{};

try
{
context.Reporter.Info() << Resource::String::InstallFlowStartingPackageInstall << std::endl;

// InstallerPath will point to a directory if it is extracted from an archive.
if (std::filesystem::is_directory(installerPath))
{
const std::vector<Manifest::NestedInstallerFile>& nestedInstallerFiles = context.Get<Execution::Data::Installer>()->NestedInstallerFiles;

for (const auto& nestedInstallerFile : nestedInstallerFiles)
{
const std::filesystem::path& fontFilePath = installerPath / ConvertToUTF16(nestedInstallerFile.RelativeFilePath);
auto fontFileEntryName = AppInstaller::Fonts::GetFontFileTitle(fontFilePath);
fontFileMap.emplace(fontFileEntryName, fontFilePath);
}
}
else
{
auto fontName = AppInstaller::Fonts::GetFontFileTitle(installerPath);
fontFileMap.emplace(fontName, installerPath);
}

fontInstaller.Install(fontFileMap);
context.Add<Execution::Data::OperationReturnCode>(ERROR_SUCCESS);
}
catch (...)
{
context.Add<Execution::Data::OperationReturnCode>(Workflow::HandleException(context, std::current_exception()));
}
}
}
6 changes: 0 additions & 6 deletions src/AppInstallerCLICore/Workflows/FontFlow.h
Original file line number Diff line number Diff line change
@@ -16,10 +16,4 @@ namespace AppInstaller::CLI::Workflow
// Inputs: Manifest, Scope, Rename, Location
// Outputs: None
void FontInstallImpl(Execution::Context& context);

// Initializes the font installer.
// Required Args: None
// Inputs: Scope, Manifest, Installer
// Outputs: None
void InitializeFontInstaller(Execution::Context& context);
}
3 changes: 2 additions & 1 deletion src/AppInstallerCLICore/Workflows/InstallFlow.cpp
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
#include "pch.h"
#include "InstallFlow.h"
#include "DownloadFlow.h"
#include "FontFlow.h"
#include "UninstallFlow.h"
#include "UpdateFlow.h"
#include "ResumeFlow.h"
@@ -284,7 +285,6 @@ namespace AppInstaller::CLI::Workflow
void FontInstall(Execution::Context& context)
{
context <<
InitializeFontInstaller <<
FontInstallImpl <<
ReportInstallerResult("Font"sv, APPINSTALLER_CLI_ERROR_FONT_INSTALL_FAILED, true);
}
@@ -457,6 +457,7 @@ namespace AppInstaller::CLI::Workflow
break;
case InstallerTypeEnum::Font:
context << details::FontInstall;
break;
default:
THROW_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
}
44 changes: 40 additions & 4 deletions src/AppInstallerCommonCore/Fonts.cpp
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@
#include <AppInstallerStrings.h>
#include <winget/Fonts.h>
#include <winget/Locale.h>
#include <ShObjIdl_core.h>
#include <propkey.h>

namespace AppInstaller::Fonts
{
@@ -49,15 +51,13 @@ namespace AppInstaller::Fonts
FontCatalog::FontCatalog()
{
m_preferredLocales = AppInstaller::Locale::GetUserPreferredLanguagesUTF16();
THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(m_factory), m_factory.put_unknown()));
}

std::vector<FontFamily> FontCatalog::GetInstalledFontFamilies(std::optional<std::wstring> familyName)
{
wil::com_ptr<IDWriteFactory7> factory;
THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), factory.put_unknown()));

wil::com_ptr<IDWriteFontCollection> collection;
THROW_IF_FAILED(factory->GetSystemFontCollection(collection.addressof(), FALSE));
THROW_IF_FAILED(m_factory->GetSystemFontCollection(collection.addressof(), FALSE));

std::vector<FontFamily> installedFontFamilies;

@@ -85,6 +85,20 @@ namespace AppInstaller::Fonts
return installedFontFamilies;
}

bool FontCatalog::IsValidFontFile(const std::filesystem::path& filePath)
{
wil::com_ptr<IDWriteFontFile> fontFile;
THROW_IF_FAILED(m_factory->CreateFontFileReference(filePath.c_str(), NULL, &fontFile));

BOOL isValid;
DWRITE_FONT_FILE_TYPE fileType;
DWRITE_FONT_FACE_TYPE faceType;
UINT32 numOfFaces;
THROW_IF_FAILED(fontFile->Analyze(&isValid, &fileType, &faceType, &numOfFaces));

return isValid;
}

std::wstring FontCatalog::GetLocalizedStringFromFont(const wil::com_ptr<IDWriteLocalizedStrings>& localizedStringCollection)
{
UINT32 index = 0;
@@ -172,4 +186,26 @@ namespace AppInstaller::Fonts
fontFamily.Faces = std::move(fontFaces);
return fontFamily;
}

std::wstring GetFontFileTitle(const std::filesystem::path& fontFilePath)
{
auto co_unitialize = wil::CoInitializeEx();
IPropertyStore* pps = nullptr;
std::wstring title;
HRESULT hr = SHGetPropertyStoreFromParsingName(fontFilePath.c_str(), nullptr, GPS_DEFAULT, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr)) {
PROPVARIANT prop;
PropVariantInit(&prop);
hr = pps->GetValue(PKEY_Title, &prop);
if (SUCCEEDED(hr)) {
title = prop.pwszVal;
PropVariantClear(&prop);
pps->Release();
}
PropVariantClear(&prop);
pps->Release();
}

return title;
}
}
9 changes: 8 additions & 1 deletion src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp
Original file line number Diff line number Diff line change
@@ -162,6 +162,10 @@ namespace AppInstaller::Manifest
{
result = InstallerTypeEnum::Portable;
}
else if (inStrLower == "font")
{
result = InstallerTypeEnum::Font;
}

return result;
}
@@ -583,6 +587,8 @@ namespace AppInstaller::Manifest
return "msstore"sv;
case InstallerTypeEnum::Portable:
return "portable"sv;
case InstallerTypeEnum::Font:
return "font"sv;
}

return "unknown"sv;
@@ -962,7 +968,8 @@ namespace AppInstaller::Manifest
nestedInstallerType == InstallerTypeEnum::Wix ||
nestedInstallerType == InstallerTypeEnum::Burn ||
nestedInstallerType == InstallerTypeEnum::Portable ||
nestedInstallerType == InstallerTypeEnum::Msix
nestedInstallerType == InstallerTypeEnum::Msix ||
nestedInstallerType == InstallerTypeEnum::Font
);
}

6 changes: 6 additions & 0 deletions src/AppInstallerCommonCore/Public/winget/Fonts.h
Original file line number Diff line number Diff line change
@@ -20,20 +20,26 @@ namespace AppInstaller::Fonts
std::vector<FontFace> Faces;
};

std::wstring GetFontFileTitle(const std::filesystem::path& fontFilePath);

struct FontCatalog
{
FontCatalog();

// Gets all installed font families on the system. If an exact family name is provided and found, returns the installed font family.
std::vector<FontFamily> GetInstalledFontFamilies(std::optional<std::wstring> familyName = {});

// Returns a boolean value indicating whether the specified file path is a valid font file.
bool IsValidFontFile(const std::filesystem::path& filePath);

private:
FontFamily GetFontFamilyByIndex(const wil::com_ptr<IDWriteFontCollection>& collection, UINT32 index);
std::wstring GetLocalizedStringFromFont(const wil::com_ptr<IDWriteLocalizedStrings>& localizedStringCollection);
std::wstring GetFontFamilyName(const wil::com_ptr<IDWriteFontFamily>& fontFamily);
std::wstring GetFontFaceName(const wil::com_ptr<IDWriteFont>& font);
Utility::OpenTypeFontVersion GetFontFaceVersion(const wil::com_ptr<IDWriteFont>& font);

wil::com_ptr<IDWriteFactory7> m_factory;
std::vector<std::wstring> m_preferredLocales;
};
}