Skip to content

Manage Windows OCR Language Settings #512

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Text-Grab/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
Expand Down Expand Up @@ -234,6 +235,9 @@ private void appExit(object sender, ExitEventArgs e)

private async void appStartup(object sender, StartupEventArgs e)
{
if (checkForAdminWindow(e.Args))
return;

NumberOfRunningInstances = Process.GetProcessesByName("Text-Grab").Length;
Current.DispatcherUnhandledException += CurrentDispatcherUnhandledException;

Expand Down Expand Up @@ -272,6 +276,23 @@ private async void appStartup(object sender, StartupEventArgs e)
DefaultLaunch();
}

private bool checkForAdminWindow(string[] args)
{
WindowCollection allWindows = Current.Windows;

foreach (Window window in allWindows)
if (window is AdminWindow)
return true;

if (args.Length == 0 || !args.Contains("admin"))
return false;

AdminWindow aw = new();
aw.Show();

return true;
}

private void CurrentDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
// unhandled exceptions thrown from UI thread
Expand Down
8 changes: 8 additions & 0 deletions Text-Grab/Pages/LanguageSettings.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@
Click="HyperlinkButton_Click"
Content="How to install OCR languages with PowerShell"
NavigateUri="https://learn.microsoft.com/en-us/windows/powertoys/text-extractor#how-to-query-for-ocr-language-packs" />
<StackPanel Margin="0,8,0,0" Orientation="Horizontal">
<Button Margin="12,0,0,0" Click="InstalWindowsLangButton_Click">
<StackPanel Orientation="Horizontal">
<ui:SymbolIcon Symbol="Shield24" />
<TextBlock Margin="6,0,0,0" Text="Manage Windows OCR Languages" />
</StackPanel>
</Button>
</StackPanel>
<!-- Show the commands to execute to add other languages -->
<ui:ListView
x:Name="WindowsLanguagesListView"
Expand Down
45 changes: 41 additions & 4 deletions Text-Grab/Pages/LanguageSettings.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@

namespace Text_Grab.Pages;

/// <summary>
/// Interaction logic for LanguageSettings.xaml
/// </summary>
public partial class LanguageSettings : Page
{
private readonly Settings DefaultSettings = AppUtilities.TextGrabSettings;
Expand Down Expand Up @@ -47,7 +44,7 @@ private async void Page_Loaded(object sender, RoutedEventArgs e)
private void LoadWindowsLanguages()
{
WindowsLanguagesListView.Items.Clear();
List<Language> possibleOCRLanguages = OcrEngine.AvailableRecognizerLanguages.ToList();
List<Language> possibleOCRLanguages = [.. OcrEngine.AvailableRecognizerLanguages];
foreach (Language language in possibleOCRLanguages)
WindowsLanguagesListView.Items.Add(language);
}
Expand Down Expand Up @@ -155,4 +152,44 @@ private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
e.Handled = true;
}

private void InstalWindowsLangButton_Click(object sender, RoutedEventArgs e)
{
if (AppContext.BaseDirectory is not string exeDir)
return;

string exePath = Path.Combine(exeDir, "Text-Grab.exe");

ProcessStartInfo startInfo = new()
{
UseShellExecute = true,
WorkingDirectory = Environment.CurrentDirectory,
FileName = exePath,
Verb = "runas",
Arguments = "admin",
WindowStyle = ProcessWindowStyle.Normal
};

try
{
Process? process = Process.Start(startInfo);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}


public record LangListItem
{
public string RightPart { get; set; } = string.Empty;

public string LeftPart { get; set; } = string.Empty;

public override string ToString()
{
return string.Join(' ', [LeftPart, RightPart]);
}
}
1 change: 1 addition & 0 deletions Text-Grab/Text-Grab.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<PackageReference Include="CliWrap" Version="3.7.0" />
<PackageReference Include="Dapplo.Windows.User32" Version="1.0.28" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Dism" Version="3.3.0" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
<PackageReference Include="WPF-UI" Version="4.0.0-rc.2" />
Expand Down
58 changes: 58 additions & 0 deletions Text-Grab/Utilities/WindowsLanguageUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
namespace Text_Grab.Utilities;
public class WindowsLanguageUtilities
{
public static string PowerShellCommandForInstallingWithTag(string languageTag)
{
// $Capability = Get-WindowsCapability -Online | Where-Object { $_.Name -Like 'Language.OCR*en-US*' }
return $"$Capability = Get-WindowsCapability -Online | Where-Object {{ $_.Name -Like 'Language.OCR*{languageTag}*' }}; $Capability | Add-WindowsCapability -Online";
}

public static string DismLanguageCommand(string languageTag)
{
return $"Language.OCR~~~{languageTag}";
}

public static string PowerShellCommandForUninstallingWithTag(string languageTag)
{
// $Capability = Get-WindowsCapability -Online | Where-Object { $_.Name -Like 'Language.OCR*en-US*' }
return $"$Capability = Get-WindowsCapability -Online | Where-Object {{ $_.Name -Like 'Language.OCR*{languageTag}*' }}; $Capability | Remove-WindowsCapability -Online";
}

public static readonly string[] AllLanguages = [
"ar-SA",
"bg-BG",
"bs-LATN-BA",
"cs-CZ",
"da-DK",
"de-DE",
"el-GR",
"en-GB",
"en-US",
"es-ES",
"es-MX",
"fi-FI",
"fr-CA",
"fr-FR",
"hr-HR",
"hu-HU",
"it-IT",
"ja-JP",
"ko-KR",
"nb-NO",
"nl-NL",
"pl-PL",
"pt-BR",
"pt-PT",
"ro-RO",
"ru-RU",
"sk-SK",
"sl-SI",
"sr-CYRL-RS",
"sr-LATN-RS",
"sv-SE",
"tr-TR",
"zh-CN",
"zh-HK",
"zh-TW",
];
}
72 changes: 72 additions & 0 deletions Text-Grab/Views/AdminWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<ui:FluentWindow
x:Class="Text_Grab.Views.AdminWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Text_Grab.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Width="800"
Height="450"
Closed="Window_Closed"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<ui:TitleBar Title="Install Windows Languages (admin)" />
<ScrollViewer Grid.Row="1">
<Border>
<StackPanel Margin="40,20">
<TextBlock Text="Welcome to Admin window!" />
<StackPanel Margin="0,8,0,0" Orientation="Horizontal">
<ComboBox
x:Name="AllWindowsLanguagesComboBox"
Width="300"
IsEditable="True"
IsTextSearchCaseSensitive="False"
IsTextSearchEnabled="True">
<ComboBox.ItemTemplate>
<DataTemplate DataType="local:LangListItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<TextBlock Margin="0,0,20,0" Text="{Binding LeftPart}" />
<TextBlock
Grid.Column="1"
Text="{Binding RightPart}"
TextAlignment="Left" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Margin="12,0,0,0" Click="InstalWindowsLangButton_Click">
<StackPanel Orientation="Horizontal">
<ui:SymbolIcon Symbol="Shield24" />
<TextBlock Margin="6,0,0,0" Text="Install" />
</StackPanel>
</Button>
</StackPanel>

<ProgressBar
x:Name="DismProgressBar"
Height="20"
Margin="0,12,0,6"
IsIndeterminate="False"
Maximum="100"
Visibility="Collapsed" />

<Button x:Name="LoadLanguages" Click="LoadLanguages_Click">Load languages</Button>
<TextBox
x:Name="DismOutputTextBlock"
Margin="0,12,0,6"
Text="DISM Output:" />
</StackPanel>
</Border>
</ScrollViewer>
</Grid>
</ui:FluentWindow>
98 changes: 98 additions & 0 deletions Text-Grab/Views/AdminWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using Microsoft.Dism;
using System;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Text_Grab.Pages;
using Text_Grab.Utilities;

namespace Text_Grab.Views;

public partial class AdminWindow : Wpf.Ui.Controls.FluentWindow
{
public AdminWindow()
{
InitializeComponent();

AllWindowsLanguagesComboBox.Items.Clear();
foreach (string textName in WindowsLanguageUtilities.AllLanguages)
{
CultureInfo languageCulture = new(textName);
string paddedTextName = textName.PadRight(12);
LangListItem langListItem = new()
{
LeftPart = paddedTextName,
RightPart = languageCulture.DisplayName
};
AllWindowsLanguagesComboBox.Items.Add(langListItem);
}

DismApi.Initialize(DismLogLevel.LogErrorsWarnings);
}

private void Window_Closed(object sender, EventArgs e)
{
DismApi.Shutdown();
Application.Current.Shutdown();
}

private async void InstalWindowsLangButton_Click(object sender, RoutedEventArgs e)
{
if (AllWindowsLanguagesComboBox.SelectedItem is not LangListItem pickedLanguageFile)
return;

using DismSession session = DismApi.OpenOnlineSession();

DismCapability? langToInstall = DismApi.GetCapabilities(session).FirstOrDefault(cap => cap.Name.Contains(pickedLanguageFile.LeftPart.Trim()));

if (langToInstall is null)
{
foreach (DismCapability cap in DismApi.GetCapabilities(session))
if (cap.Name.Contains("Language.OCR~~~"))
{
DismOutputTextBlock.Text += $"{Environment.NewLine}Dism capability: {cap.Name}";

if (cap.Name.Contains(pickedLanguageFile.LeftPart))
DismOutputTextBlock.Text += $"<--- Found {pickedLanguageFile.LeftPart}";
}

DismOutputTextBlock.Text += $"{Environment.NewLine}Language: {pickedLanguageFile.LeftPart} not found.";
return;
}

DismProgressBar.Visibility = Visibility.Visible;

DismProgressCallback progressCallback = new(progress =>
{
DismProgressBar.Maximum = progress.Total;
DismProgressBar.Value = progress.Current;
});

await Task.Run(() =>
{
DismApi.AddCapability(session, langToInstall.Name, false, null, progressCallback, null);
});

DismProgressBar.Visibility = Visibility.Collapsed;
}

private void LoadLanguages_Click(object sender, RoutedEventArgs e)
{
using DismSession session = DismApi.OpenOnlineSession();

var caps = DismApi.GetCapabilities(session);

foreach (DismCapability cap in DismApi.GetCapabilities(session))
{
string capName = cap.Name;
if (!capName.StartsWith("Language.OCR~~~"))
continue;
if (cap.State != DismPackageFeatureState.Installed)
continue;
string localeName = capName["Language.OCR~~~".Length..capName.LastIndexOf('~')];
CultureInfo culture = new(localeName);
DismOutputTextBlock.Text += $"{Environment.NewLine}{localeName} - {culture.DisplayName} - {capName}";
}
}
}
Loading