DocumentViewList.cs 32 KB

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