This commit is contained in:
Benji Shohet 2025-07-30 20:28:31 +03:00 committed by GitHub
commit bd2c73a596
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 72 additions and 27 deletions

View file

@ -1,13 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<!-- Including language XML files to copy to the output directory for localization. -->
<ItemGroup> <ItemGroup>
<None Include="Languages\language*.xml"> <None Include="Languages\language*.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<!-- Adding NuGet package references required by the project. -->
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" version="7.1.2" /> <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" version="7.1.2" />
</ItemGroup> </ItemGroup>
<!-- Adding project references/dependencies for the project. -->
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" /> <ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" />
</ItemGroup> </ItemGroup>

View file

@ -43,6 +43,10 @@ namespace Greenshot.Plugin.Win10
private readonly string _imageFilePath; private readonly string _imageFilePath;
/// <summary>
/// Initializes a new instance of the ToastNotificationService class.
/// If the program was started by a toast notification, logs this information.
/// </summary>
public ToastNotificationService() public ToastNotificationService()
{ {
if (ToastNotificationManagerCompat.WasCurrentProcessToastActivated()) if (ToastNotificationManagerCompat.WasCurrentProcessToastActivated())
@ -80,12 +84,12 @@ namespace Greenshot.Plugin.Win10
} }
/// <summary> /// <summary>
/// This creates the actual toast /// Creates and displays the actual toast notification with the specified message and parameters.
/// </summary> /// </summary>
/// <param name="message">string</param> /// <param name="message">The string message to display in the toast notification.</param>
/// <param name="timeout">TimeSpan until the toast timeouts</param> /// <param name="timeout">TimeSpan until the toast timeouts or should stay on screen, or null for the default duration.</param>
/// <param name="onClickAction">Action called when clicked</param> /// <param name="onClickAction">Action called when clicked, or null for no action.</param>
/// <param name="onClosedAction">Action called when the toast is closed</param> /// <param name="onClosedAction">Action called when the toast is closed, or null for no action.</param>
private void ShowMessage(string message, TimeSpan? timeout = default, Action onClickAction = null, Action onClosedAction = null) private void ShowMessage(string message, TimeSpan? timeout = default, Action onClickAction = null, Action onClosedAction = null)
{ {
// Do not inform the user if this is disabled // Do not inform the user if this is disabled
@ -210,7 +214,7 @@ namespace Greenshot.Plugin.Win10
} }
/// <summary> /// <summary>
/// Factory method, helping with checking if the notification service is even available /// Factory method, helping with checking if the notification service is even available - Creates a new instance of the ToastNotificationService class, if supported on the current system.
/// </summary> /// </summary>
/// <returns>ToastNotificationService</returns> /// <returns>ToastNotificationService</returns>
public static ToastNotificationService Create() public static ToastNotificationService Create()

View file

@ -29,6 +29,9 @@ namespace Greenshot.Plugin.Win10
[IniSection("Win10", Description = "Greenshot Win10 Plugin configuration")] [IniSection("Win10", Description = "Greenshot Win10 Plugin configuration")]
public class Win10Configuration : IniSection public class Win10Configuration : IniSection
{ {
/// <summary>
/// Gets or sets a value indicating whether OCR should be run automatically on every capture.
/// </summary>
[IniProperty("AlwaysRunOCROnCapture", Description = "Determines if OCR is run automatically on every capture", DefaultValue = "False")] [IniProperty("AlwaysRunOCROnCapture", Description = "Determines if OCR is run automatically on every capture", DefaultValue = "False")]
public bool AlwaysRunOCROnCapture { get; set; } public bool AlwaysRunOCROnCapture { get; set; }
} }

View file

@ -41,7 +41,10 @@ namespace Greenshot.Plugin.Win10
/// </summary> /// </summary>
public class Win10OcrProvider : IOcrProvider public class Win10OcrProvider : IOcrProvider
{ {
// Log for debugging and information
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10OcrProvider)); private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10OcrProvider));
// Minimum width and height for the OCR to work correctly
private const int MinWidth = 130; private const int MinWidth = 130;
private const int MinHeight = 130; private const int MinHeight = 130;
@ -50,7 +53,10 @@ namespace Greenshot.Plugin.Win10
/// </summary> /// </summary>
public Win10OcrProvider() public Win10OcrProvider()
{ {
// Get available languages from the OCR engine and log them
var languages = OcrEngine.AvailableRecognizerLanguages; var languages = OcrEngine.AvailableRecognizerLanguages;
// Log all available languages for OCR.
foreach (var language in languages) foreach (var language in languages)
{ {
Log.DebugFormat("Found language {0} {1}", language.NativeName, language.LanguageTag); Log.DebugFormat("Found language {0} {1}", language.NativeName, language.LanguageTag);
@ -64,19 +70,26 @@ namespace Greenshot.Plugin.Win10
/// <returns>OcrResult sync</returns> /// <returns>OcrResult sync</returns>
public async Task<OcrInformation> DoOcrAsync(ISurface surface) public async Task<OcrInformation> DoOcrAsync(ISurface surface)
{ {
// Will contain the result of the OCR process
OcrInformation result; OcrInformation result;
// Using a memory stream to handle the image
using (var imageStream = new MemoryStream()) using (var imageStream = new MemoryStream())
{ {
// We only want the background // We only want the background
// Output settings for the image before OCR process
var outputSettings = new SurfaceOutputSettings(OutputFormat.png, 0, true) var outputSettings = new SurfaceOutputSettings(OutputFormat.png, 0, true)
{ {
ReduceColors = true, ReduceColors = true,
SaveBackgroundOnly = true SaveBackgroundOnly = true
}; };
// Force Grayscale output // Force Grayscale output to the image
outputSettings.Effects.Add(new GrayscaleEffect()); outputSettings.Effects.Add(new GrayscaleEffect());
// If the surface is smaller than the minimum dimensions, resize it
if (surface.Image.Width < MinWidth || surface.Image.Height < MinHeight) if (surface.Image.Width < MinWidth || surface.Image.Height < MinHeight)
{ {
// Calculate dimensions to add
int addedWidth = MinWidth - surface.Image.Width; int addedWidth = MinWidth - surface.Image.Width;
if (addedWidth < 0) if (addedWidth < 0)
{ {
@ -95,13 +108,18 @@ namespace Greenshot.Plugin.Win10
{ {
addedHeight /= 2; addedHeight /= 2;
} }
// Add a resize effect to the image
IEffect effect = new ResizeCanvasEffect(addedWidth, addedWidth, addedHeight, addedHeight); IEffect effect = new ResizeCanvasEffect(addedWidth, addedWidth, addedHeight, addedHeight);
outputSettings.Effects.Add(effect); outputSettings.Effects.Add(effect);
} }
// Save the surface to the stream and reset position
ImageIO.SaveToStream(surface, imageStream, outputSettings); ImageIO.SaveToStream(surface, imageStream, outputSettings);
imageStream.Position = 0; imageStream.Position = 0;
// Create a random access stream from the memory stream
var randomAccessStream = imageStream.AsRandomAccessStream(); var randomAccessStream = imageStream.AsRandomAccessStream();
// Perform OCR on the stream
result = await DoOcrAsync(randomAccessStream); result = await DoOcrAsync(randomAccessStream);
} }
@ -158,8 +176,10 @@ namespace Greenshot.Plugin.Win10
{ {
var result = new OcrInformation(); var result = new OcrInformation();
// Iterate over all lines in the OCR result
foreach (var ocrLine in ocrResult.Lines) foreach (var ocrLine in ocrResult.Lines)
{ {
// Create a new line and add it to the result
var line = new Line(ocrLine.Words.Count) var line = new Line(ocrLine.Words.Count)
{ {
Text = ocrLine.Text Text = ocrLine.Text
@ -167,12 +187,16 @@ namespace Greenshot.Plugin.Win10
result.Lines.Add(line); result.Lines.Add(line);
// Loop through each word in the line and process it.
for (var index = 0; index < ocrLine.Words.Count; index++) for (var index = 0; index < ocrLine.Words.Count; index++)
{ {
var ocrWord = ocrLine.Words[index]; var ocrWord = ocrLine.Words[index];
// Create the bounding rectangle for the word
var location = new NativeRect((int) ocrWord.BoundingRect.X, (int) ocrWord.BoundingRect.Y, var location = new NativeRect((int) ocrWord.BoundingRect.X, (int) ocrWord.BoundingRect.Y,
(int) ocrWord.BoundingRect.Width, (int) ocrWord.BoundingRect.Height); (int) ocrWord.BoundingRect.Width, (int) ocrWord.BoundingRect.Height);
// Add the word to the line
var word = line.Words[index]; var word = line.Words[index];
word.Text = ocrWord.Text; word.Text = ocrWord.Text;
word.Bounds = location; word.Bounds = location;

View file

@ -61,6 +61,8 @@ namespace Greenshot.Plugin.Win10
/// </summary> /// </summary>
/// <returns>true if plugin is initialized, false if not (doesn't show)</returns> /// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
public bool Initialize() public bool Initialize()
{
try
{ {
// Here we check if the build version of Windows is actually what we support // Here we check if the build version of Windows is actually what we support
if (!WindowsVersion.IsWindows10BuildOrLater(17763)) if (!WindowsVersion.IsWindows10BuildOrLater(17763))
@ -78,8 +80,15 @@ namespace Greenshot.Plugin.Win10
// Add the destinations // Add the destinations
SimpleServiceProvider.Current.AddService<IDestination>(new Win10OcrDestination()); SimpleServiceProvider.Current.AddService<IDestination>(new Win10OcrDestination());
SimpleServiceProvider.Current.AddService<IDestination>(new Win10ShareDestination()); SimpleServiceProvider.Current.AddService<IDestination>(new Win10ShareDestination());
return true; return true;
} }
catch (Exception e)
{
Log.Error("Failed to initialize Win10Plugin. Error: " + e.ToString(), e);
return false;
}
}
public void Shutdown() public void Shutdown()
{ {