DocumentViewList.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885
  1. using Comal.Classes;
  2. using InABox.Clients;
  3. using InABox.Core;
  4. using InABox.Wpf;
  5. using InABox.WPF;
  6. using PRSDesktop.Panels.DataEntry;
  7. using Syncfusion.Pdf;
  8. using Syncfusion.Pdf.Parsing;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Collections.ObjectModel;
  12. using System.ComponentModel;
  13. using System.Drawing;
  14. using System.Drawing.Drawing2D;
  15. using System.Drawing.Imaging;
  16. using System.IO;
  17. using System.Linq;
  18. using System.Reflection;
  19. using System.Runtime.CompilerServices;
  20. using System.Text;
  21. using System.Threading.Tasks;
  22. using System.Windows;
  23. using System.Windows.Controls;
  24. using System.Windows.Data;
  25. using System.Windows.Input;
  26. using System.Windows.Media;
  27. using System.Windows.Media.Imaging;
  28. using Syncfusion.OCRProcessor;
  29. using Syncfusion.Pdf.Graphics;
  30. using Color = System.Drawing.Color;
  31. using Image = System.Windows.Controls.Image;
  32. using Rectangle = System.Windows.Shapes.Rectangle;
  33. namespace PRSDesktop;
  34. public class DocumentOCRContextMenuArgs(ContextMenu menu, string text) : EventArgs
  35. {
  36. public ContextMenu Menu { get; private set; } = menu;
  37. public string Text { get; private set; } = text;
  38. }
  39. public delegate void DocumentOCRContextMenuOpeningEvent(object sender, DocumentOCRContextMenuArgs args);
  40. /// <summary>
  41. /// Control that allows to view a list of documents, within a zoom control, and providing methods to rotate/explode data.
  42. /// </summary>
  43. /// <remarks>
  44. /// This is originally from the Data entry panel, and this implementation is a <i>little bit</i> scuffed. Basically, because the <see cref="DataEntryDocument"/>
  45. /// is not an <see cref="EntityDocument{T}"/>, there is no good shared interface, so I made this abstract, with a type argument. Then to get the "EntityDocument"
  46. /// ID or the Document ID, there are abstract functions.
  47. /// <b/>
  48. /// Note one needs also to provide <see cref="UpdateDocument"/>. This is a function used by the "Rotate Image" button, and its implementation needs
  49. /// to update the THumbnail of the Entity Document, and save it, along with refreshing the view list.
  50. /// </remarks>
  51. /// <typeparam name="TDocument"></typeparam>
  52. public abstract class DocumentViewList<TDocument> : UserControl, INotifyPropertyChanged
  53. {
  54. public static readonly DependencyProperty CanRotateImageProperty = DependencyProperty.Register(nameof(CanRotateImage), typeof(bool), typeof(DocumentViewList<TDocument>), new PropertyMetadata(true, CanRotateImage_Changed));
  55. private static void CanRotateImage_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
  56. {
  57. if (d is not DocumentViewList<TDocument> list) return;
  58. list.DoPropertyChanged(e.Property.Name);
  59. }
  60. private IList<TDocument> _documents = [];
  61. public IList<TDocument> Documents
  62. {
  63. get => _documents;
  64. set
  65. {
  66. UpdateViewList(value);
  67. }
  68. }
  69. private readonly object _viewListLock = new object();
  70. private class ViewDocument
  71. {
  72. public ImageSource Image { get; set; }
  73. public TDocument Document { get; set; }
  74. public int PageNumber { get; set; }
  75. public ViewDocument(ImageSource image, TDocument document, int page)
  76. {
  77. Image = image;
  78. Document = document;
  79. PageNumber = page;
  80. }
  81. }
  82. private List<ViewDocument> ViewDocuments { get; } = new();
  83. public ObservableCollection<ImageSource> ViewList { get; init; } = new();
  84. private ZoomPanel ZoomPanel;
  85. private bool _canExplode;
  86. public bool CanExplode
  87. {
  88. get => _canExplode;
  89. set
  90. {
  91. _canExplode = value;
  92. DoPropertyChanged();
  93. }
  94. }
  95. public event Action? Explode;
  96. public event Action? ExplodeAll;
  97. public event Action<TDocument, Document>? UpdateDocument;
  98. public bool CanRotateImage
  99. {
  100. get => (bool)GetValue(CanRotateImageProperty);
  101. set => SetValue(CanRotateImageProperty, value);
  102. }
  103. private static OCRProcessor? processor = null;
  104. public DocumentViewList()
  105. {
  106. var tesseractpath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
  107. "Tesseract");
  108. if (Directory.Exists(tesseractpath))
  109. {
  110. processor ??= new OCRProcessor(tesseractpath);
  111. processor.Settings.Language = Languages.English;
  112. processor.Settings.PageSegment = PageSegMode.SparseText;
  113. var fontfile = Path.Combine(tesseractpath, "ARIALUNI.ttf");
  114. if (File.Exists(fontfile))
  115. {
  116. try
  117. {
  118. using (var fontStream = new FileStream(fontfile, FileMode.Open))
  119. processor.UnicodeFont = new PdfTrueTypeFont(fontStream, 8);
  120. }
  121. catch (Exception e)
  122. {
  123. Logger.Send(LogType.Error,"",$"Unable to Load ARIALUNI.TTF: {e.Message}\n{e.StackTrace}");
  124. }
  125. }
  126. }
  127. var border = new Border();
  128. border.BorderBrush = Colors.Gray.ToBrush();
  129. border.Background = Colors.DimGray.ToBrush();
  130. ZoomPanel = new ZoomPanel();
  131. var itemsControl = new ItemsControl();
  132. itemsControl.Margin = new Thickness(10);
  133. itemsControl.ItemsSource = ViewList;
  134. var factory = new FrameworkElementFactory(typeof(StackPanel));
  135. factory.SetValue(StackPanel.OrientationProperty, Orientation.Vertical);
  136. itemsControl.ItemsPanel = new ItemsPanelTemplate(factory);
  137. itemsControl.ContextMenu = new ContextMenu();
  138. var explode = itemsControl.ContextMenu.AddItem("Regroup Pages", null, Explode_Click);
  139. explode.Bind(VisibilityProperty, this, x => x.CanExplode, new InABox.WPF.BooleanToVisibilityConverter(Visibility.Visible, Visibility.Collapsed));
  140. var explodeAll = itemsControl.ContextMenu.AddItem("Explode All Pages", null, ExplodeAll_Click);
  141. explodeAll.Bind(VisibilityProperty, this, x => x.CanExplode, new InABox.WPF.BooleanToVisibilityConverter(Visibility.Visible, Visibility.Collapsed));
  142. var viewImage = new MenuItem()
  143. {
  144. Header = "View Image"
  145. };
  146. viewImage.ToolTip = "Show this image in a separate window.";
  147. viewImage.Bind<ImageSource, ImageSource>(MenuItem.TagProperty, x => x);
  148. viewImage.Click += ViewImage_Click;
  149. itemsControl.ContextMenu.Items.Add(viewImage);
  150. var rotateImage = new MenuItem()
  151. {
  152. Header = "Rotate Document"
  153. };
  154. rotateImage.ToolTip = "Rotate this document 90° clockwise";
  155. rotateImage.Bind<ImageSource, ImageSource>(MenuItem.TagProperty, x => x);
  156. rotateImage.SetBinding(MenuItem.IsEnabledProperty, new Binding("CanRotateImage") { Source = this });
  157. rotateImage.Click += RotateImage_Click;
  158. itemsControl.ContextMenu.Items.Add(rotateImage);
  159. //var ocrImage = new MenuItem()
  160. //{
  161. // Header = "OCR Document (Preview)"
  162. //};
  163. //ocrImage.ToolTip = "Extract Text from this Image (Preview Only)";
  164. //ocrImage.Bind<ImageSource, ImageSource>(MenuItem.TagProperty, x => x);
  165. //ocrImage.Click += OCRImage_Click;
  166. //itemsControl.ContextMenu.Items.Add(ocrImage);
  167. itemsControl.ItemTemplate = TemplateGenerator.CreateDataTemplate(() =>
  168. {
  169. var grid = new Grid();
  170. System.Windows.Point? dragStartPoint = null;
  171. var dragObject = new Border()
  172. {
  173. BorderThickness = new Thickness(0.75),
  174. BorderBrush = System.Windows.Media.Brushes.Firebrick,
  175. Background = new SolidColorBrush(Colors.Red)
  176. {
  177. Opacity = 0.2
  178. },
  179. Visibility = Visibility.Collapsed
  180. };
  181. ContextMenu dragMenu = new ContextMenu();
  182. dragMenu.Closed += (s, e) =>
  183. {
  184. Logger.Send(LogType.Information,"","DragMenu Closed");
  185. dragStartPoint = null;
  186. dragObject.Visibility = Visibility.Collapsed;
  187. };
  188. var img = new Image();
  189. img.Bind<ImageSource, ImageSource>(Image.SourceProperty, x => x);
  190. img.Margin = new(0, 0, 0, 5);
  191. img.ContextMenu = itemsControl.ContextMenu;
  192. img.PreviewMouseLeftButtonDown += (sender, args) =>
  193. {
  194. if (sender is not Image image)
  195. return;
  196. if(args.ClickCount >= 2)
  197. {
  198. OpenImageWindow(image.Source);
  199. args.Handled = true;
  200. }
  201. if (processor == null || dragStartPoint != null || dragMenu.IsOpen)
  202. return;
  203. Logger.Send(LogType.Information,"",$"Starting Drag - {sender.GetType()}");
  204. dragStartPoint = args.GetPosition(img);
  205. Logger.Send(LogType.Information,"",$"Setting DragObject to be visible");
  206. dragObject.Visibility = Visibility.Visible;
  207. Logger.Send(LogType.Information,"",$"Set DragObject to be visible");
  208. dragObject.Margin = new Thickness(dragStartPoint.Value.X, dragStartPoint.Value.Y, img.ActualWidth-dragStartPoint.Value.X, img.ActualHeight-dragStartPoint.Value.Y);
  209. };
  210. img.PreviewMouseMove += (sender, args) =>
  211. {
  212. if (processor == null || dragStartPoint == null || dragMenu.IsOpen)
  213. return;
  214. Logger.Send(LogType.Information,"",$"MouseMove with DragObject set");
  215. var point = args.GetPosition(img);
  216. var top = Math.Min(point.Y, dragStartPoint.Value.Y);
  217. var left = Math.Min(point.X, dragStartPoint.Value.X);
  218. var bottom = Math.Max(point.Y, dragStartPoint.Value.Y);
  219. var right = Math.Max(point.X, dragStartPoint.Value.X);
  220. Logger.Send(LogType.Information,"",$"Drag Rectangle is {left},{top} -> {right},{bottom}");
  221. dragObject.Margin = new Thickness(left+1, top+1, (img.ActualWidth-right)+1, (img.ActualHeight-bottom)+1);
  222. };
  223. img.PreviewMouseLeftButtonUp += (sender, args) =>
  224. {
  225. if (processor == null)
  226. return;
  227. Logger.Send(LogType.Information,"",$"Hiding DragObject");
  228. if (dragStartPoint == null)
  229. return;
  230. Logger.Send(LogType.Information,"",$"Ending Drag");
  231. if (sender is not Image image || image.Source is not BitmapSource bitmapSource)
  232. return;
  233. var croprect = new RectangleF(
  234. (float)dragObject.Margin.Left-0.75F,
  235. (float)dragObject.Margin.Top-0.75F,
  236. (float)(img.ActualWidth+0.75F-(dragObject.Margin.Left+dragObject.Margin.Right)),
  237. (float)(img.ActualHeight+0.75F-(dragObject.Margin.Top+dragObject.Margin.Bottom))
  238. );
  239. if (croprect.Width < 5 || croprect.Height < 5)
  240. {
  241. dragStartPoint = null;
  242. dragObject.Visibility = Visibility.Collapsed;
  243. return;
  244. }
  245. var bitmap = ImageUtils.BitmapSourceToBitmap(bitmapSource);
  246. Bitmap bmp = bitmap.Clone(croprect, bitmap.PixelFormat);
  247. bmp = Resize(bmp, bmp.Width * 10, bmp.Height * 10);
  248. bmp = SetGrayscale(bmp);
  249. bmp = RemoveNoise(bmp);
  250. bmp.Save(Path.Combine(CoreUtils.GetPath(),"ocr.bmp"));
  251. string text = processor.PerformOCR(bmp, CoreUtils.GetPath()).Trim();
  252. if (string.IsNullOrWhiteSpace(text))
  253. {
  254. dragStartPoint = null;
  255. dragObject.Visibility = Visibility.Collapsed;
  256. return;
  257. }
  258. dragMenu.Items.Clear();
  259. OCRContextMenuOpening?.Invoke(this, new DocumentOCRContextMenuArgs(dragMenu, text));
  260. if (dragMenu.Items.Count == 0)
  261. {
  262. dragStartPoint = null;
  263. dragObject.Visibility = Visibility.Collapsed;
  264. return;
  265. }
  266. dragMenu.Items.Insert(0,new MenuItem() { Header = text, IsEnabled = false});
  267. dragMenu.Items.Insert(1,new Separator());
  268. dragMenu.IsOpen = true;
  269. };
  270. img.MouseUp += (sender, args) =>
  271. {
  272. Logger.Send(LogType.Information, "", $"MouseUp");
  273. };
  274. grid.Children.Add(img);
  275. grid.Children.Add(dragObject);
  276. return grid;
  277. });
  278. ZoomPanel.Content = itemsControl;
  279. border.Child = ZoomPanel;
  280. Content = border;
  281. BindingOperations.EnableCollectionSynchronization(ViewList, _viewListLock);
  282. }
  283. public event DocumentOCRContextMenuOpeningEvent OCRContextMenuOpening;
  284. protected abstract Guid GetID(TDocument document);
  285. protected abstract Guid GetDocumentID(TDocument document);
  286. protected abstract string GetDocumentFileName(IEnumerable<TDocument> documents, Document document);
  287. protected abstract IEnumerable<Document> LoadDocuments(IEnumerable<Guid> ids);
  288. public void UpdateViewList(IList<TDocument> documents, bool force = false)
  289. {
  290. if (!force && documents.Count == _documents.Count && !documents.Any(x => _documents.All(y => GetID(x) != GetID(y))))
  291. return;
  292. _documents = documents;
  293. ViewList.Clear();
  294. ViewDocuments.Clear();
  295. if(_documents.Count == 0)
  296. {
  297. return;
  298. }
  299. Task.Run(() =>
  300. {
  301. var docs = LoadDocuments(Documents.Select(GetDocumentID).Distinct());
  302. foreach (var doc in docs)
  303. doc.FileName = GetDocumentFileName(Documents, doc);
  304. LoadDocuments(docs);
  305. }).ContinueWith((task) =>
  306. {
  307. if(task.Exception is not null)
  308. {
  309. MessageWindow.ShowError("An error occurred while loading the documents", task.Exception);
  310. }
  311. }, TaskScheduler.FromCurrentSynchronizationContext());
  312. }
  313. private void LoadDocuments(IEnumerable<Document> documents)
  314. {
  315. var bitmaps = new Dictionary<Guid, List<ImageSource>>();
  316. foreach (var document in documents.Where(x=>x.Data?.Any() == true))
  317. {
  318. List<byte[]> images;
  319. var bitmapImages = new List<ImageSource>();
  320. var extension = Path.GetExtension(document.FileName).ToLower();
  321. if (extension == ".pdf")
  322. {
  323. images = new List<byte[]>();
  324. try
  325. {
  326. bitmapImages = ImageUtils.RenderPDFToImageSources(document.Data);
  327. }
  328. catch (Exception e)
  329. {
  330. MessageBox.Show($"Cannot load document '{document.FileName}': {e.Message}");
  331. }
  332. }
  333. else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp")
  334. {
  335. images = new List<byte[]> { document.Data };
  336. }
  337. else
  338. {
  339. images = ImageUtils.RenderTextFileToImages(Encoding.UTF8.GetString(document.Data));
  340. }
  341. bitmapImages.AddRange(images.Select(x =>
  342. {
  343. try
  344. {
  345. return ImageUtils.LoadImage(x);
  346. }
  347. catch (Exception e)
  348. {
  349. Dispatcher.BeginInvoke(() =>
  350. {
  351. MessageWindow.ShowError($"Cannot load document '{document.FileName}", e);
  352. });
  353. }
  354. return null;
  355. }).Where(x => x != null).Cast<ImageSource>());
  356. foreach (var image in bitmapImages)
  357. {
  358. if (!bitmaps.TryGetValue(document.ID, out var list))
  359. {
  360. list = new List<ImageSource>();
  361. bitmaps[document.ID] = list;
  362. }
  363. list.Add(image);
  364. }
  365. }
  366. ViewDocuments.Clear();
  367. var maxWidth = 0.0;
  368. foreach (var scan in Documents)
  369. {
  370. if (bitmaps.TryGetValue(GetDocumentID(scan), out var list))
  371. {
  372. int page = 1;
  373. foreach (var bitmap in list)
  374. {
  375. maxWidth = Math.Max(maxWidth, bitmap.Width);
  376. ViewDocuments.Add(new(bitmap, scan, page));
  377. page++;
  378. }
  379. }
  380. }
  381. lock (_viewListLock)
  382. {
  383. ViewList.Clear();
  384. foreach(var doc in ViewDocuments)
  385. {
  386. ViewList.Add(doc.Image);
  387. }
  388. if(maxWidth != 0.0)
  389. {
  390. ZoomPanel.Scale = ZoomPanel.ActualWidth / (maxWidth * 1.1);
  391. ZoomPanel.MinScale = ZoomPanel.Scale / 2;
  392. }
  393. }
  394. }
  395. private void RotateDocument(Document doc, int pageNumber)
  396. {
  397. var extension = Path.GetExtension(doc.FileName).ToLower();
  398. if (extension == ".pdf")
  399. {
  400. var loadeddoc = new PdfLoadedDocument(doc.Data);
  401. bool allPages = loadeddoc.PageCount() > 1;
  402. if (allPages)
  403. {
  404. allPages = MessageWindow.New()
  405. .Message("Do you want to rotate all pages in this PDF?")
  406. .Title("Rotate all?")
  407. .AddYesButton("All pages")
  408. .AddNoButton("Just this page")
  409. .Display().Result == MessageWindowResult.Yes;
  410. }
  411. if(allPages)
  412. {
  413. foreach (var page in loadeddoc.GetPages())
  414. {
  415. var rotation = (int)page.Rotation;
  416. rotation = (rotation + 1) % 4;
  417. page.Rotation = (PdfPageRotateAngle)rotation;
  418. }
  419. }
  420. else if(pageNumber <= loadeddoc.PageCount())
  421. {
  422. var page = loadeddoc.GetPage(pageNumber - 1);
  423. var rotation = (int)page.Rotation;
  424. rotation = (rotation + 1) % 4;
  425. page.Rotation = (PdfPageRotateAngle)rotation;
  426. }
  427. doc.Data = loadeddoc.SaveToBytes();
  428. }
  429. else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp")
  430. {
  431. using var stream = new MemoryStream(doc.Data);
  432. var bitmap = Bitmap.FromStream(stream);
  433. bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
  434. using var outStream = new MemoryStream();
  435. bitmap.Save(outStream, extension switch
  436. {
  437. ".jpg" or ".jpeg" => ImageFormat.Jpeg,
  438. ".png" => ImageFormat.Png,
  439. _ => ImageFormat.Bmp
  440. });
  441. doc.Data = outStream.ToArray();
  442. }
  443. else
  444. {
  445. using var stream = new MemoryStream(doc.Data);
  446. var loadeddoc = DataEntryReGroupWindow.RenderToPDF(doc.FileName, stream);
  447. foreach (var page in loadeddoc.GetPages())
  448. {
  449. var rotation = (int)page.Rotation;
  450. rotation = (rotation + 1) % 4;
  451. page.Rotation = (PdfPageRotateAngle)rotation;
  452. }
  453. doc.Data = loadeddoc.SaveToBytes();
  454. }
  455. }
  456. public Bitmap Resize(Bitmap bmp, int newWidth, int newHeight)
  457. {
  458. Bitmap result = new Bitmap(newWidth, newHeight);
  459. using (Graphics g = Graphics.FromImage(result))
  460. {
  461. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  462. g.DrawImage(bmp, 0, 0, newWidth, newHeight);
  463. }
  464. return result;
  465. // Bitmap temp = (Bitmap)bmp;
  466. //
  467. // Bitmap bmap = new Bitmap(newWidth, newHeight, temp.PixelFormat);
  468. //
  469. // double nWidthFactor = (double)temp.Width / (double)newWidth;
  470. // double nHeightFactor = (double)temp.Height / (double)newHeight;
  471. //
  472. // double fx, fy, nx, ny;
  473. // int cx, cy, fr_x, fr_y;
  474. // Color color1 = new Color();
  475. // Color color2 = new Color();
  476. // Color color3 = new Color();
  477. // Color color4 = new Color();
  478. // byte nRed, nGreen, nBlue;
  479. //
  480. // byte bp1, bp2;
  481. //
  482. // for (int x = 0; x < bmap.Width; ++x)
  483. // {
  484. // for (int y = 0; y < bmap.Height; ++y)
  485. // {
  486. //
  487. // fr_x = (int)Math.Floor(x * nWidthFactor);
  488. // fr_y = (int)Math.Floor(y * nHeightFactor);
  489. // cx = fr_x + 1;
  490. // if (cx >= temp.Width) cx = fr_x;
  491. // cy = fr_y + 1;
  492. // if (cy >= temp.Height) cy = fr_y;
  493. // fx = x * nWidthFactor - fr_x;
  494. // fy = y * nHeightFactor - fr_y;
  495. // nx = 1.0 - fx;
  496. // ny = 1.0 - fy;
  497. //
  498. // color1 = temp.GetPixel(fr_x, fr_y);
  499. // color2 = temp.GetPixel(cx, fr_y);
  500. // color3 = temp.GetPixel(fr_x, cy);
  501. // color4 = temp.GetPixel(cx, cy);
  502. //
  503. // // Blue
  504. // bp1 = (byte)(nx * color1.B + fx * color2.B);
  505. //
  506. // bp2 = (byte)(nx * color3.B + fx * color4.B);
  507. //
  508. // nBlue = (byte)(ny * (double)(bp1) + fy * (double)(bp2));
  509. //
  510. // // Green
  511. // bp1 = (byte)(nx * color1.G + fx * color2.G);
  512. //
  513. // bp2 = (byte)(nx * color3.G + fx * color4.G);
  514. //
  515. // nGreen = (byte)(ny * (double)(bp1) + fy * (double)(bp2));
  516. //
  517. // // Red
  518. // bp1 = (byte)(nx * color1.R + fx * color2.R);
  519. //
  520. // bp2 = (byte)(nx * color3.R + fx * color4.R);
  521. //
  522. // nRed = (byte)(ny * (double)(bp1) + fy * (double)(bp2));
  523. //
  524. // bmap.SetPixel(x, y, System.Drawing.Color.FromArgb
  525. // (255, nRed, nGreen, nBlue));
  526. // }
  527. // }
  528. //
  529. //
  530. //
  531. // return bmap;
  532. }
  533. public Bitmap SetGrayscale(Bitmap img)
  534. {
  535. Bitmap temp = (Bitmap)img;
  536. Bitmap bmap = (Bitmap)temp.Clone();
  537. Color c;
  538. for (int i = 0; i < bmap.Width; i++)
  539. {
  540. for (int j = 0; j < bmap.Height; j++)
  541. {
  542. c = bmap.GetPixel(i, j);
  543. byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);
  544. bmap.SetPixel(i, j, Color.FromArgb(gray, gray, gray));
  545. }
  546. }
  547. return (Bitmap)bmap.Clone();
  548. }
  549. public Bitmap RemoveNoise(Bitmap bmap)
  550. {
  551. for (var x = 0; x < bmap.Width; x++)
  552. {
  553. for (var y = 0; y < bmap.Height; y++)
  554. {
  555. var pixel = bmap.GetPixel(x, y);
  556. if (pixel.R < 162 && pixel.G < 162 && pixel.B < 162)
  557. bmap.SetPixel(x, y, Color.Black);
  558. else if (pixel.R > 162 && pixel.G > 162 && pixel.B > 162)
  559. bmap.SetPixel(x, y, Color.White);
  560. }
  561. }
  562. return bmap;
  563. }
  564. // private async void OCRImage_Click(object sender, RoutedEventArgs e)
  565. // {
  566. // if (sender is not MenuItem item || item.Tag is not ImageSource image) return;
  567. //
  568. // var document = ViewDocuments.FirstOrDefault(x => x.Image == image);
  569. // if (document is null)
  570. // {
  571. // MessageWindow.ShowError("An error occurred", "Document does not exist in ViewDocuments list");
  572. // return;
  573. // }
  574. //
  575. // var doc = LoadDocuments(CoreUtils.One(GetDocumentID(document.Document))).First();
  576. //
  577. // if (doc.FileName.ToLower().EndsWith(".txt"))
  578. // {
  579. // var text = System.Text.Encoding.UTF8.GetString(doc.Data);
  580. // File.WriteAllText(Path.Combine(CoreUtils.GetPath(),"txt-ocr.txt"),text);
  581. // return;
  582. // }
  583. // else
  584. // {
  585. // List<String> pagetexts = new List<String>();
  586. // var images = doc.FileName.ToLower().EndsWith(".pdf")
  587. // ? ImageUtils.RenderPDFToImageBytes(doc.Data, ImageUtils.ImageEncoding.PNG).ToList()
  588. // : new List<byte[]>() { doc.Data };
  589. //
  590. // using (var fontStream = new FileStream(Path.Combine(CoreUtils.GetPath(), "ARIALUNI.ttf"), FileMode.Open))
  591. // {
  592. // using (var font = new PdfTrueTypeFont(fontStream, 8))
  593. // {
  594. // using (OCRProcessor processor = new OCRProcessor(CoreUtils.GetPath()))
  595. // {
  596. // processor.Settings.Language = Languages.English;
  597. // processor.Settings.PageSegment = PageSegMode.SparseText;
  598. // processor.Settings.OCREngineMode = OCREngineMode.LSTMOnly;
  599. // //processor.UnicodeFont = font;
  600. // foreach (var img in images)
  601. // {
  602. // using (var ms = new MemoryStream(img))
  603. // {
  604. // using (var bitmap = new Bitmap(ms))
  605. // {
  606. // var bmp = Resize(bitmap, bitmap.Width * 3, bitmap.Height * 3);
  607. // bmp = SetGrayscale(bmp);
  608. // bmp = RemoveNoise(bmp);
  609. // bmp.Save(Path.Combine(CoreUtils.GetPath(),"ocr.bmp"));
  610. // string text = processor.PerformOCR(bitmap, CoreUtils.GetPath());
  611. // pagetexts.Add(text);
  612. // }
  613. // }
  614. //
  615. // }
  616. // }
  617. // }
  618. //
  619. // fontStream.Close();
  620. // }
  621. // File.WriteAllText(Path.Combine(CoreUtils.GetPath(), "image-ocr.txt"), String.Join("\n\n",pagetexts));
  622. //
  623. // }
  624. //
  625. // try
  626. // {
  627. // // TextRecognizer? _textRecognizer;
  628. // // var readyState = TextRecognizer.GetReadyState();
  629. // // if (readyState is AIFeatureReadyState.NotSupportedOnCurrentSystem or AIFeatureReadyState.DisabledByUser)
  630. // // return;
  631. // //
  632. // // if (readyState == AIFeatureReadyState.NotReady)
  633. // // await TextRecognizer.EnsureReadyAsync();
  634. // //
  635. // // _textRecognizer = await TextRecognizer.CreateAsync();
  636. // // var bitmap = await ByteArrayToSoftwareBitmapAsync(doc.Data);
  637. // //
  638. // // SoftwareBitmap displayableImage = SoftwareBitmap.Convert(bitmap,
  639. // // BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
  640. // //
  641. // // var imageBuffer = ImageBuffer.CreateForSoftwareBitmap(displayableImage);
  642. // // var result = _textRecognizer.RecognizeTextFromImage(imageBuffer);
  643. // // File.WriteAllText(Path.Combine(CoreUtils.GetPath(),"ai-ocr.txt"),String.Join("\n",result.Lines.Select(x=>x.Text)));
  644. //
  645. //
  646. // // else
  647. // // {
  648. // // var images = doc.FileName.ToLower().EndsWith(".pdf")
  649. // // ? ImageUtils.RenderPDFToImageBytes(doc.Data).ToList()
  650. // // : new List<byte[]>() { doc.Data };
  651. // //
  652. // // using (OCRProcessor processor = new OCRProcessor(CoreUtils.GetPath()))
  653. // // {
  654. // // PdfLoadedDocument pdfLoadedDocument = new PdfLoadedDocument(doc.Data);
  655. // // processor.Settings.Language = Languages.English;
  656. // // var text = processor.PerformOCR(pdfLoadedDocument);
  657. // // File.WriteAllText(Path.Combine(CoreUtils.GetPath(),"pdf-ocr.txt"),text);
  658. // // }
  659. // // }
  660. // // else
  661. // // {
  662. // // using (var stream = new MemoryStream(doc.Data))
  663. // // {
  664. // // using (var bmp = new Bitmap(stream))
  665. // // {
  666. // // using (var fontStream = new FileStream(Path.Combine(CoreUtils.GetPath(), "ARIALUNI.ttf"),
  667. // // FileMode.Open))
  668. // // {
  669. // // using (var font = new PdfTrueTypeFont(fontStream, 8))
  670. // // {
  671. // // using (OCRProcessor processor = new OCRProcessor(CoreUtils.GetPath()))
  672. // // {
  673. // // processor.Settings.Language = Languages.English;
  674. // // processor.UnicodeFont = font;
  675. // // string text = processor.PerformOCR(bmp, CoreUtils.GetPath());
  676. // // File.WriteAllText(Path.Combine(CoreUtils.GetPath(), "image-ocr.txt"), text);
  677. // // }
  678. // // }
  679. // //
  680. // // fontStream.Close();
  681. // // }
  682. // // }
  683. // // }
  684. // // }}
  685. // }
  686. // catch(Exception err)
  687. // {
  688. // MessageWindow.ShowError("Something went wrong while trying to scan this document.", err);
  689. // return;
  690. // }
  691. //
  692. // }
  693. private void RotateImage_Click(object sender, RoutedEventArgs e)
  694. {
  695. if (sender is not MenuItem item || item.Tag is not ImageSource image) return;
  696. var document = ViewDocuments.FirstOrDefault(x => x.Image == image);
  697. if (document is null)
  698. {
  699. MessageWindow.ShowError("An error occurred", "Document does not exist in ViewDocuments list");
  700. return;
  701. }
  702. var doc = LoadDocuments(CoreUtils.One(GetDocumentID(document.Document))).First();
  703. try
  704. {
  705. RotateDocument(doc, document.PageNumber);
  706. }
  707. catch(Exception err)
  708. {
  709. MessageWindow.ShowError("Something went wrong while trying to rotate this document.", err);
  710. return;
  711. }
  712. Client.Save(doc, "Rotated by user.");
  713. UpdateDocument?.Invoke(document.Document, doc);
  714. }
  715. private void ViewImage_Click(object sender, RoutedEventArgs e)
  716. {
  717. if (sender is not MenuItem item || item.Tag is not ImageSource image) return;
  718. OpenImageWindow(image);
  719. }
  720. private void ExplodeAll_Click()
  721. {
  722. ExplodeAll?.Invoke();
  723. }
  724. private void Explode_Click()
  725. {
  726. Explode?.Invoke();
  727. }
  728. #region Image Window
  729. private List<DataEntryDocumentWindow> OpenWindows = new();
  730. public void CloseImageWindows()
  731. {
  732. while (OpenWindows.Count > 0)
  733. {
  734. var win = OpenWindows.Last();
  735. OpenWindows.RemoveAt(OpenWindows.Count - 1);
  736. win.Close();
  737. }
  738. }
  739. private void OpenImageWindow(ImageSource image)
  740. {
  741. var window = OpenWindows.FirstOrDefault(x => x.Images.Contains(image));
  742. if (window is not null)
  743. {
  744. window.Activate();
  745. }
  746. else
  747. {
  748. var docID = GetDocumentID(ViewDocuments.First(x => x.Image == image).Document);
  749. var docs = ViewDocuments.Where(x => GetDocumentID(x.Document) == docID);
  750. window = new DataEntryDocumentWindow();
  751. window.Topmost = true;
  752. foreach(var doc in docs)
  753. {
  754. window.Images.Add(doc.Image);
  755. }
  756. OpenWindows.Add(window);
  757. window.Closed += OpenWindow_Closed;
  758. window.Show();
  759. }
  760. }
  761. private void OpenWindow_Closed(object? sender, EventArgs e)
  762. {
  763. if (sender is not DataEntryDocumentWindow window) return;
  764. OpenWindows.Remove(window);
  765. }
  766. #endregion
  767. public event PropertyChangedEventHandler? PropertyChanged;
  768. protected void DoPropertyChanged([CallerMemberName] string propertyName = "")
  769. {
  770. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  771. }
  772. }