diff --git a/src/AppInstallerCLICore/FontInstaller.cpp b/src/AppInstallerCLICore/FontInstaller.cpp index 90a7d58033..e045e75ad0 100644 --- a/src/AppInstallerCLICore/FontInstaller.cpp +++ b/src/AppInstallerCLICore/FontInstaller.cpp @@ -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 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); + } } } diff --git a/src/AppInstallerCLICore/FontInstaller.h b/src/AppInstallerCLICore/FontInstaller.h index fb46ff19cb..1168185889 100644 --- a/src/AppInstallerCLICore/FontInstaller.h +++ b/src/AppInstallerCLICore/FontInstaller.h @@ -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 fontFiles); private: Manifest::ScopeEnum m_scope; - std::string m_familyName; + std::filesystem::path m_installLocation; + Registry::Key m_key; }; } diff --git a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp index 456525e2ee..cf483626ed 100644 --- a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp @@ -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)); } diff --git a/src/AppInstallerCLICore/Workflows/FontFlow.cpp b/src/AppInstallerCLICore/Workflows/FontFlow.cpp index f3defe3395..1d6c7ee8c1 100644 --- a/src/AppInstallerCLICore/Workflows/FontFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/FontFlow.cpp @@ -5,10 +5,12 @@ #include "TableOutput.h" #include #include +#include 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(); + std::map 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& nestedInstallerFiles = context.Get()->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(ERROR_SUCCESS); + } + catch (...) + { + context.Add(Workflow::HandleException(context, std::current_exception())); + } } } diff --git a/src/AppInstallerCLICore/Workflows/FontFlow.h b/src/AppInstallerCLICore/Workflows/FontFlow.h index ce98099b5a..5ad5e0a2e8 100644 --- a/src/AppInstallerCLICore/Workflows/FontFlow.h +++ b/src/AppInstallerCLICore/Workflows/FontFlow.h @@ -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); } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index ae633643cd..dcd299340e 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -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)); } diff --git a/src/AppInstallerCommonCore/Fonts.cpp b/src/AppInstallerCommonCore/Fonts.cpp index 6f5f328f93..08625d1623 100644 --- a/src/AppInstallerCommonCore/Fonts.cpp +++ b/src/AppInstallerCommonCore/Fonts.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include 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 FontCatalog::GetInstalledFontFamilies(std::optional familyName) { - wil::com_ptr factory; - THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), factory.put_unknown())); - wil::com_ptr collection; - THROW_IF_FAILED(factory->GetSystemFontCollection(collection.addressof(), FALSE)); + THROW_IF_FAILED(m_factory->GetSystemFontCollection(collection.addressof(), FALSE)); std::vector installedFontFamilies; @@ -85,6 +85,20 @@ namespace AppInstaller::Fonts return installedFontFamilies; } + bool FontCatalog::IsValidFontFile(const std::filesystem::path& filePath) + { + wil::com_ptr 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& 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; + } } diff --git a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp index 16e759441c..491d165722 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp @@ -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 ); } diff --git a/src/AppInstallerCommonCore/Public/winget/Fonts.h b/src/AppInstallerCommonCore/Public/winget/Fonts.h index 443dea5f7a..396c36a58e 100644 --- a/src/AppInstallerCommonCore/Public/winget/Fonts.h +++ b/src/AppInstallerCommonCore/Public/winget/Fonts.h @@ -20,6 +20,8 @@ namespace AppInstaller::Fonts std::vector Faces; }; + std::wstring GetFontFileTitle(const std::filesystem::path& fontFilePath); + struct FontCatalog { FontCatalog(); @@ -27,6 +29,9 @@ namespace AppInstaller::Fonts // Gets all installed font families on the system. If an exact family name is provided and found, returns the installed font family. std::vector GetInstalledFontFamilies(std::optional 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& collection, UINT32 index); std::wstring GetLocalizedStringFromFont(const wil::com_ptr& localizedStringCollection); @@ -34,6 +39,7 @@ namespace AppInstaller::Fonts std::wstring GetFontFaceName(const wil::com_ptr& font); Utility::OpenTypeFontVersion GetFontFaceVersion(const wil::com_ptr& font); + wil::com_ptr m_factory; std::vector m_preferredLocales; }; }