diff --git a/CharacterMap/CharacterMap.CX/CustomFontManager.cpp b/CharacterMap/CharacterMap.CX/CustomFontManager.cpp index 98b45bde..989bc24b 100644 --- a/CharacterMap/CharacterMap.CX/CustomFontManager.cpp +++ b/CharacterMap/CharacterMap.CX/CustomFontManager.cpp @@ -108,7 +108,7 @@ ComPtr CustomFontManager::GetFontCollectionFromPath(Plat ComPtr tcollection; ComPtr collection; - auto factory = GetIsolatedFactory(); + auto& factory = GetIsolatedFactory(); ThrowIfFailed(factory->CreateCustomFontCollection(m_customLoader.Get(), key, keySize, &tcollection)); tcollection.As(&collection); diff --git a/CharacterMap/CharacterMap.CX/DWHelpers.h b/CharacterMap/CharacterMap.CX/DWHelpers.h index c170690f..1f2aeca5 100644 --- a/CharacterMap/CharacterMap.CX/DWHelpers.h +++ b/CharacterMap/CharacterMap.CX/DWHelpers.h @@ -13,9 +13,9 @@ template void SafeRelease(T** ppT) namespace { /// -/// Opposite of DWRITE_MAKE_OPENTYPE_TAG, returns String -/// representation of OpenType tag. -/// + /// Opposite of DWRITE_MAKE_OPENTYPE_TAG, returns String + /// representation of OpenType tag. + /// Platform::String^ GetOpenTypeFeatureTag(UINT32 value) { wchar_t buffer[] = L" "; diff --git a/CharacterMap/CharacterMap.CX/DWriteFontAxis.h b/CharacterMap/CharacterMap.CX/DWriteFontAxis.h index 04cf304d..4af5bbdf 100644 --- a/CharacterMap/CharacterMap.CX/DWriteFontAxis.h +++ b/CharacterMap/CharacterMap.CX/DWriteFontAxis.h @@ -86,10 +86,7 @@ namespace CharacterMapCX DWRITE_FONT_AXIS_VALUE GetDWriteValue() { - auto value = DWRITE_FONT_AXIS_VALUE(); - value.axisTag = static_cast(m_tag_raw); - value.value = this->Value; - return value; + return { static_cast(m_tag_raw), this->Value }; } private: diff --git a/CharacterMap/CharacterMap.CX/DWriteProperties.h b/CharacterMap/CharacterMap.CX/DWriteProperties.h index 2f02ac75..aa479c1c 100644 --- a/CharacterMap/CharacterMap.CX/DWriteProperties.h +++ b/CharacterMap/CharacterMap.CX/DWriteProperties.h @@ -69,7 +69,7 @@ namespace CharacterMapCX property Array^ Panose { Array^ get() { - DWRITE_PANOSE* pan = new DWRITE_PANOSE(); + DWRITE_PANOSE pan[10]; m_font->GetPanose(pan); bool valid = false; diff --git a/CharacterMap/CharacterMap.CX/DirectText.cpp b/CharacterMap/CharacterMap.CX/DirectText.cpp index edc34168..d392de24 100644 --- a/CharacterMap/CharacterMap.CX/DirectText.cpp +++ b/CharacterMap/CharacterMap.CX/DirectText.cpp @@ -138,7 +138,10 @@ Windows::Foundation::Size CharacterMapCX::Controls::DirectText::MeasureOverride( /* Set flow direction */ if (this->FlowDirection == Windows::UI::Xaml::FlowDirection::RightToLeft) - idFormat->SetFlowDirection(DWRITE_FLOW_DIRECTION::DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT); + idFormat->SetReadingDirection(DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + else + idFormat->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT); + /* Set blank fallback font */ if (FallbackFont != nullptr) diff --git a/CharacterMap/CharacterMap.CX/NativeInterop.cpp b/CharacterMap/CharacterMap.CX/NativeInterop.cpp index 8396d2d9..e39b7d60 100644 --- a/CharacterMap/CharacterMap.CX/NativeInterop.cpp +++ b/CharacterMap/CharacterMap.CX/NativeInterop.cpp @@ -321,8 +321,10 @@ ComPtr CharacterMapCX::NativeInterop::CreateIDWriteTextForma L"en-us", &tempFormat); + tempFormat->SetFlowDirection(DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM); + tempFormat->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT); + ComPtr idFormat; ThrowIfFailed(tempFormat.As(&idFormat)); - return idFormat; } diff --git a/CharacterMap/CharacterMap/Controls/SettingsPresenter.cs b/CharacterMap/CharacterMap/Controls/SettingsPresenter.cs index dc6e430e..e9de56ea 100644 --- a/CharacterMap/CharacterMap/Controls/SettingsPresenter.cs +++ b/CharacterMap/CharacterMap/Controls/SettingsPresenter.cs @@ -39,14 +39,14 @@ public object Title DependencyProperty.Register(nameof(Title), typeof(object), typeof(SettingsPresenter), new PropertyMetadata(null)); - public string Description + public object Description { - get { return (string)GetValue(DescriptionProperty); } + get { return (object)GetValue(DescriptionProperty); } set { SetValue(DescriptionProperty, value); } } public static readonly DependencyProperty DescriptionProperty = - DependencyProperty.Register(nameof(Description), typeof(string), typeof(SettingsPresenter), new PropertyMetadata(null, (d,e) => + DependencyProperty.Register(nameof(Description), typeof(object), typeof(SettingsPresenter), new PropertyMetadata(null, (d,e) => { ((SettingsPresenter)d).UpdateDescriptionStates(); })); @@ -219,7 +219,7 @@ void UpdateIconStates() private void UpdateDescriptionStates() { - string state = string.IsNullOrWhiteSpace(Description) ? "NoDescriptionState" : "DescriptionState"; + string state = Description is null ? "NoDescriptionState" : "DescriptionState"; VisualStateManager.GoToState(this, state, true); } diff --git a/CharacterMap/CharacterMap/Core/Converters.cs b/CharacterMap/CharacterMap/Core/Converters.cs index 904015af..863dd3b7 100644 --- a/CharacterMap/CharacterMap/Core/Converters.cs +++ b/CharacterMap/CharacterMap/Core/Converters.cs @@ -95,6 +95,12 @@ public static GridLength GridLengthAorB(bool input, string a, string b) public static string GetLocalizedEnumName(Enum a) => Localization.Get($"{a.GetType().Name}_{a}"); + public static string GetFormat(string key, object arg) + => Localization.Get(key, arg); + + public static string GetFormat2(string key, object arg, object arg2) + => Localization.Get(key, arg, arg2); + public static FontFamily GetPreviewFontSource(FontVariant variant) { if (_settings == null) diff --git a/CharacterMap/CharacterMap/Core/ExportManager.Font.cs b/CharacterMap/CharacterMap/Core/ExportManager.Font.cs index 3d8d9491..5105775e 100644 --- a/CharacterMap/CharacterMap/Core/ExportManager.Font.cs +++ b/CharacterMap/CharacterMap/Core/ExportManager.Font.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Windows.Storage.Streams; using Windows.Storage; @@ -46,13 +45,19 @@ public static async void RequestExportFontFile(FontVariant variant) WeakReferenceMessenger.Default.Send(new AppNotificationMessage(true, new ExportFontFileResult(null, false))); } - internal static Task ExportCollectionAsZipAsync(IList fontList, UserFontCollection selectedCollection) + internal static Task ExportCollectionAsZipAsync( + IList fontList, + UserFontCollection selectedCollection, + Action callback = null) { var fonts = fontList.SelectMany(f => f.Variants).ToList(); - return ExportFontsAsZipAsync(fonts, selectedCollection.Name); + return ExportFontsAsZipAsync(fonts, selectedCollection.Name, callback); } - internal static async Task ExportFontsAsZipAsync(List fonts, string name) + internal static async Task ExportFontsAsZipAsync( + List fonts, + string name, + Action callback = null) { if (await PickFileAsync(name, "ZIP", new[] { ".zip" }) is StorageFile file) { @@ -63,7 +68,10 @@ await Task.Run(async () => using var i = await file.OpenStreamForWriteAsync(); i.SetLength(0); + int c = 0; using ZipArchive z = new(i, ZipArchiveMode.Create); + callback?.Invoke($"0%"); + foreach (var font in fonts) { if (DirectWrite.IsFontLocal(font.Face)) @@ -73,6 +81,9 @@ await Task.Run(async () => using IOutputStream s = entry.Open().AsOutputStream(); await DirectWrite.WriteToStreamAsync(font.Face, s); } + + c++; + callback?.Invoke($"{((double)c / (double)fonts.Count) * 100:0}%"); } await i.FlushAsync(); @@ -82,20 +93,26 @@ await Task.Run(async () => } } - internal static Task ExportCollectionToFolderAsync(IList fontList) + internal static Task ExportCollectionToFolderAsync( + IList fontList, + Action callback = null) { var fonts = fontList.SelectMany(f => f.Variants).ToList(); - return ExportFontsToFolderAsync(fonts); + return ExportFontsToFolderAsync(fonts, callback); } - internal static async Task ExportFontsToFolderAsync(List fonts) + internal static async Task ExportFontsToFolderAsync( + List fonts, + Action callback = null) { if (await PickFolderAsync() is StorageFolder folder) { await Task.Run(async () => { ExportNamingScheme scheme = ResourceHelper.AppSettings.ExportNamingScheme; + callback?.Invoke($"0%"); + int c = 0; foreach (var font in fonts) { if (DirectWrite.IsFontLocal(font.Face)) @@ -104,6 +121,9 @@ await Task.Run(async () => StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false); await TryWriteToFileAsync(font, file).ConfigureAwait(false); } + + c++; + callback?.Invoke($"{((double)c / (double)fonts.Count) * 100:0}%"); } }); @@ -144,6 +164,9 @@ private static string GetFileName(FontVariant font, ExportNamingScheme scheme) if (scheme == ExportNamingScheme.System && !string.IsNullOrWhiteSpace(src)) fileName = src; + if (scheme is ExportNamingScheme.Optimised && FontFinder.ImportFormats.Contains(ext) is false) + ext = ".ttf"; + if (string.IsNullOrWhiteSpace(fileName)) fileName = $"{font.FamilyName.Trim()} {font.PreferredName.Trim()}{ext}"; diff --git a/CharacterMap/CharacterMap/Core/FontFinder.cs b/CharacterMap/CharacterMap/Core/FontFinder.cs index 704c47d7..92756347 100644 --- a/CharacterMap/CharacterMap/Core/FontFinder.cs +++ b/CharacterMap/CharacterMap/Core/FontFinder.cs @@ -102,6 +102,9 @@ public static async Task InitialiseAsync() return systemFonts; } + public static int SystemFamilyCount { get; private set; } + public static int SystemFaceCount { get; private set; } + public static Task LoadFontsAsync(bool clearExisting = true) { // It's possible to go down this path if the font collection @@ -119,8 +122,10 @@ public static Task LoadFontsAsync(bool clearExisting = true) await _loadSemaphore.WaitAsync().ConfigureAwait(false); NativeInterop interop = Utils.GetInterop(); +#if DEBUG Stopwatch s = new Stopwatch(); s.Start(); +#endif // Load SystemFonts and imported fonts in parallel // 1.1. Load imported fonts @@ -166,6 +171,9 @@ public static Task LoadFontsAsync(bool clearExisting = true) var imports = resultList.ToDictionary(d => d.Key, v => v.Value.Clone()); /* Add all system fonts */ + SystemFamilyCount = systemFonts.Families.Count; + SystemFaceCount = systemFonts.Fonts.Count; + foreach (var font in systemFonts.Fonts) AddFont(resultList, font); @@ -177,8 +185,10 @@ public static Task LoadFontsAsync(bool clearExisting = true) if (Fallback == null) Fallback = interop.CreateEmptyFallback(); +#if DEBUG s.Stop(); var elasped = s.Elapsed; +#endif _loadSemaphore.Release(); @@ -217,6 +227,12 @@ internal static List GetImportedVariants() .ToList(); } + internal static List GetSystemVariants() + { + return Fonts.SelectMany(f => f.Variants.Where(v => v.IsImported is false)) + .ToList(); + } + /* * Helper method for adding fonts. */ diff --git a/CharacterMap/CharacterMap/Core/Properties.cs b/CharacterMap/CharacterMap/Core/Properties.cs index eb6d154d..92464aac 100644 --- a/CharacterMap/CharacterMap/Core/Properties.cs +++ b/CharacterMap/CharacterMap/Core/Properties.cs @@ -1296,5 +1296,37 @@ public static void SetRenderScale(DependencyObject obj, double value) })); #endregion + + #region Flyout + + public static FlyoutBase GetFlyout(DependencyObject obj) + { + return (FlyoutBase)obj.GetValue(FlyoutProperty); + } + + public static void SetFlyout(DependencyObject obj, FlyoutBase value) + { + obj.SetValue(FlyoutProperty, value); + } + + public static readonly DependencyProperty FlyoutProperty = + DependencyProperty.RegisterAttached("Flyout", typeof(FlyoutBase), typeof(Properties), new PropertyMetadata(null, (d,e) => + { + if (d is ButtonBase b) + { + b.Click -= ButtonFlyoutClick; + b.Click += ButtonFlyoutClick; + } + })); + + private static void ButtonFlyoutClick(object sender, RoutedEventArgs e) + { + if (sender is ButtonBase b && GetFlyout(b) is FlyoutBase fly) + { + fly.ShowAt(b); + } + } + + #endregion } } diff --git a/CharacterMap/CharacterMap/Helpers/ResourceHelper.cs b/CharacterMap/CharacterMap/Helpers/ResourceHelper.cs index 9f568df5..e066a681 100644 --- a/CharacterMap/CharacterMap/Helpers/ResourceHelper.cs +++ b/CharacterMap/CharacterMap/Helpers/ResourceHelper.cs @@ -341,6 +341,9 @@ private void Element_Unloaded(object sender, RoutedEventArgs e) public void Update() { + if (DesignMode.DesignModeEnabled) + return; + ResourceHelper.TryResolveThemeStyle3(_element); return; diff --git a/CharacterMap/CharacterMap/Provider/SQLiteGlyphProvider.cs b/CharacterMap/CharacterMap/Provider/SQLiteGlyphProvider.cs index 7fd892f0..e0f23334 100644 --- a/CharacterMap/CharacterMap/Provider/SQLiteGlyphProvider.cs +++ b/CharacterMap/CharacterMap/Provider/SQLiteGlyphProvider.cs @@ -10,6 +10,7 @@ using Windows.ApplicationModel; using Windows.UI.Xaml.Controls; using CharacterMap.Models; +using System.Diagnostics; namespace CharacterMap.Provider { @@ -174,9 +175,9 @@ private Task> InternalSearchAsync(string ftsTable, str // 1.3. If the search is ambiguous we should still search for description matches, // otherwise we can return right now - if (!ambiguous) - return hexresults; - else + //if (!ambiguous) + // return hexresults; + //else { hexResult = hexresults.Cast().FirstOrDefault(); break; @@ -187,10 +188,10 @@ private Task> InternalSearchAsync(string ftsTable, str // 1.4. If the search is ambiguous we should still search for description matches, // otherwise we can return right now with no hex results // If we are a generic symbol font, that's all folks. Time to leave. - if (!ambiguous) - { - return GlyphService.EMPTY_SEARCH; - } + //if (!ambiguous) + //{ + // return GlyphService.EMPTY_SEARCH; + //} } // 2. If we're performing SQL, create the base query filter @@ -204,12 +205,20 @@ private Task> InternalSearchAsync(string ftsTable, str /// We don't want to throw an exception if we ever hit this case, we'll just do our best. foreach (var range in variant.UnicodeRanges.Take(995)) { + string qr = range.First != range.Last + ? "Ix BETWEEN {0} AND {1}" + : "Ix == {0}"; + if (next) - sb.AppendFormat(" OR Ix BETWEEN {0} AND {1}", range.First, range.Last); + sb.AppendFormat(range.First != range.Last + ? " OR Ix BETWEEN {0} AND {1}" + : " OR Ix == {0}", range.First, range.Last); else { next = true; - sb.AppendFormat("WHERE (Ix BETWEEN {0} AND {1}", range.First, range.Last); + sb.AppendFormat(range.First != range.Last + ? "WHERE (Ix BETWEEN {0} AND {1}" + : "WHERE (Ix == {0}", range.First, range.Last); } } sb.Append(")"); @@ -248,6 +257,25 @@ List InsertHex(List list) extra = string.Format(" AND Ix NOT IN ({0})", string.Join(", ", results.Select(r => r.UnicodeIndex))); } + /// BENCHMARKING FOR FTFS VS NORMAL TABLES + //Stopwatch sw = Stopwatch.StartNew(); + //object res = null; + //for (int i = 0; i < 100; i++) + //{ + // string bsql2 = $"SELECT * FROM {table} {sb.ToString()} AND Description LIKE ? LIMIT {limit}"; + // res = _connection.GetGlyphData(table, bsql2, $"%{query}%"); + //} + //var time = sw.ElapsedMilliseconds; + //sw.Restart(); + //for (int i = 0; i < 100; i++) + //{ + // string bsql = $"SELECT * FROM {ftsTable} {sb}{extra} AND Description MATCH ? LIMIT {limit}"; + // res = _connection.GetGlyphData(ftsTable, bsql, $"{query}*"); + //} + //var time2 = sw.ElapsedMilliseconds; + //Debugger.Break(); + + // 3.3. Execute! string sql = $"SELECT * FROM {ftsTable} {sb}{extra} AND Description MATCH ? LIMIT {limit}"; results.AddRange(_connection.GetGlyphData(ftsTable, sql, $"{query}*")); diff --git a/CharacterMap/CharacterMap/Strings/en-US/Resources.resw b/CharacterMap/CharacterMap/Strings/en-US/Resources.resw index 2dec75ab..a6ab15e1 100644 --- a/CharacterMap/CharacterMap/Strings/en-US/Resources.resw +++ b/CharacterMap/CharacterMap/Strings/en-US/Resources.resw @@ -1435,4 +1435,13 @@ Tap and hold to arrange. Vietnamese + + {0} Font Families, {1} Font Faces installed on your computer + + + Installed Fonts + + + Export Installed Fonts + \ No newline at end of file diff --git a/CharacterMap/CharacterMap/Themes/ClassicThemeStyles.xaml b/CharacterMap/CharacterMap/Themes/ClassicThemeStyles.xaml index c40defcb..fdd414f6 100644 --- a/CharacterMap/CharacterMap/Themes/ClassicThemeStyles.xaml +++ b/CharacterMap/CharacterMap/Themes/ClassicThemeStyles.xaml @@ -107,6 +107,13 @@ + + + + + +