DataEntryList.xaml.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. using Comal.Classes;
  2. using InABox.Clients;
  3. using InABox.Core;
  4. using Syncfusion.Pdf.Graphics;
  5. using Syncfusion.Pdf.Parsing;
  6. using Syncfusion.Pdf;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Drawing.Imaging;
  10. using System.Drawing;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Threading.Tasks;
  15. using System.Windows;
  16. using System.Windows.Media;
  17. using System.Windows.Media.Imaging;
  18. using InABox.DynamicGrid;
  19. using InABox.WPF;
  20. using Encoder = System.Drawing.Imaging.Encoder;
  21. using Path = System.IO.Path;
  22. using Image = System.Windows.Controls.Image;
  23. using System.ComponentModel;
  24. using System.Windows.Controls;
  25. using Clipboard = System.Windows.Clipboard;
  26. using DataFormats = System.Windows.DataFormats;
  27. using DragDropEffects = System.Windows.DragDropEffects;
  28. using DragEventArgs = System.Windows.DragEventArgs;
  29. using MessageBox = System.Windows.MessageBox;
  30. using UserControl = System.Windows.Controls.UserControl;
  31. using InABox.Wpf;
  32. using System.Collections.ObjectModel;
  33. using System.Windows.Data;
  34. using PRSDesktop.Panels.DataEntry;
  35. using InABox.Wpf.Editors;
  36. using System.Timers;
  37. using Microsoft.Win32;
  38. using javax.xml.crypto;
  39. namespace PRSDesktop;
  40. public static class PDFExtensions
  41. {
  42. public static IEnumerable<PdfPageBase> GetPages(this PdfDocumentBase doc)
  43. {
  44. if (doc is PdfLoadedDocument lDoc)
  45. return lDoc.Pages.Cast<PdfPageBase>();
  46. if (doc is PdfDocument pdfDoc)
  47. return pdfDoc.Pages.Cast<PdfPageBase>();
  48. throw new Exception($"Unsupported PDF Document type {doc.GetType()}");
  49. }
  50. public static PdfPageBase GetPage(this PdfDocumentBase doc, int index)
  51. {
  52. if (doc is PdfLoadedDocument lDoc)
  53. return lDoc.Pages[index];
  54. if (doc is PdfDocument pdfDoc)
  55. return pdfDoc.Pages[index];
  56. throw new Exception($"Unsupported PDF Document type {doc.GetType()}");
  57. }
  58. public static int PageCount(this PdfDocumentBase doc)
  59. {
  60. if (doc is PdfLoadedDocument lDoc)
  61. return lDoc.Pages.Count;
  62. if (doc is PdfDocument pdfDoc)
  63. return pdfDoc.Pages.Count;
  64. throw new Exception($"Unsupported PDF Document type {doc.GetType()}");
  65. }
  66. public static PdfLoadedDocument AsLoadedDocument(this PdfDocumentBase doc)
  67. {
  68. if (doc is PdfLoadedDocument lDoc)
  69. return lDoc;
  70. if (doc is PdfDocument pdfDoc)
  71. {
  72. using var ms = new MemoryStream();
  73. pdfDoc.Save(ms);
  74. var array = ms.ToArray();
  75. return new PdfLoadedDocument(array);
  76. }
  77. throw new Exception($"Unsupported PDF Document type {doc.GetType()}");
  78. }
  79. public static byte[] SaveToBytes(this PdfDocumentBase doc)
  80. {
  81. using var ms = new MemoryStream();
  82. doc.Save(ms);
  83. return ms.ToArray();
  84. }
  85. }
  86. /// <summary>
  87. /// Interaction logic for ScanPanel.xaml
  88. /// </summary>
  89. public partial class DataEntryList : UserControl, ICorePanel, IDockPanel
  90. {
  91. private List<DataEntryDocument> SelectedScans = new();
  92. public delegate void DateEntrySelectionHandler(String appliesTo, Guid entityID, bool allowprocess);
  93. public event DateEntrySelectionHandler? SelectionChanged;
  94. private readonly object _viewListLock = new object();
  95. private List<Tuple<ImageSource, DataEntryDocument>> ViewDocuments { get; } = new();
  96. public ObservableCollection<ImageSource> ViewList { get; init; } = new();
  97. private List<DataEntryDocumentWindow> OpenWindows = new();
  98. public DataEntryList()
  99. {
  100. BindingOperations.EnableCollectionSynchronization(ViewList, _viewListLock);
  101. InitializeComponent();
  102. }
  103. #region Panel
  104. public void Setup()
  105. {
  106. _dataEntryGrid.HiddenColumns.Add(x => x.Document.ID);
  107. _dataEntryGrid.Refresh(true, false);
  108. _historyGrid.Refresh(true, false);
  109. }
  110. public void Refresh()
  111. {
  112. if (_pages.SelectedIndex == 0)
  113. _dataEntryGrid.Refresh(false, true);
  114. else if (_pages.SelectedIndex == 1)
  115. _historyGrid.Refresh(false,true);
  116. }
  117. public void Shutdown(CancelEventArgs? cancel)
  118. {
  119. CloseImageWindows();
  120. }
  121. #endregion
  122. #region View List
  123. private static List<byte[]> RenderTextFile(string textData)
  124. {
  125. var pdfDocument = new PdfDocument();
  126. var page = pdfDocument.Pages.Add();
  127. var font = new PdfStandardFont(PdfFontFamily.Courier, 14);
  128. var textElement = new PdfTextElement(textData, font);
  129. var layoutFormat = new PdfLayoutFormat
  130. {
  131. Layout = PdfLayoutType.Paginate,
  132. Break = PdfLayoutBreakType.FitPage
  133. };
  134. textElement.Draw(page, new RectangleF(0, 0, page.GetClientSize().Width, page.GetClientSize().Height), layoutFormat);
  135. using var docStream = new MemoryStream();
  136. pdfDocument.Save(docStream);
  137. var loadeddoc = new PdfLoadedDocument(docStream.ToArray());
  138. Bitmap[] bmpImages = loadeddoc.ExportAsImage(0, loadeddoc.Pages.Count - 1);
  139. var jpgEncoder = ImageUtils.GetEncoder(ImageFormat.Jpeg)!;
  140. var quality = Encoder.Quality;
  141. var encodeParams = new EncoderParameters(1);
  142. encodeParams.Param[0] = new EncoderParameter(quality, 100L);
  143. var images = new List<byte[]>();
  144. if (bmpImages != null)
  145. foreach (var image in bmpImages)
  146. {
  147. using var data = new MemoryStream();
  148. image.Save(data, jpgEncoder, encodeParams);
  149. images.Add(data.ToArray());
  150. }
  151. return images;
  152. }
  153. private void UpdateViewList(bool force = false)
  154. {
  155. var selected = _dataEntryGrid.SelectedRows.Select(x => x.ToObject<DataEntryDocument>()).ToList();
  156. if (!force && selected.Count == SelectedScans.Count && !selected.Any(x => SelectedScans.All(y => x.ID != y.ID)))
  157. return;
  158. SelectedScans = selected;
  159. ViewList.Clear();
  160. ViewDocuments.Clear();
  161. Task.Run(() =>
  162. {
  163. var docs = DataEntryCache.Cache.LoadDocuments(SelectedScans.Select(x => x.Document.ID).Distinct(), checkTimestamp: true);
  164. LoadDocuments(docs);
  165. }).ContinueWith((task) =>
  166. {
  167. if(task.Exception is not null)
  168. {
  169. MessageWindow.ShowError("An error occurred while loading the documents", task.Exception);
  170. }
  171. }, TaskScheduler.FromCurrentSynchronizationContext());
  172. }
  173. private void LoadDocuments(IEnumerable<Document> documents)
  174. {
  175. var bitmaps = new Dictionary<Guid, List<ImageSource>>();
  176. foreach (var document in documents)
  177. {
  178. List<byte[]> images;
  179. var bitmapImages = new List<ImageSource>();
  180. var extension = Path.GetExtension(document.FileName).ToLower();
  181. if (extension == ".pdf")
  182. {
  183. images = new List<byte[]>();
  184. try
  185. {
  186. bitmapImages = ImageUtils.RenderPDFToImageSources(document.Data);
  187. }
  188. catch (Exception e)
  189. {
  190. MessageBox.Show($"Cannot load document '{document.FileName}': {e.Message}");
  191. }
  192. }
  193. else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp")
  194. {
  195. images = new List<byte[]> { document.Data };
  196. }
  197. else
  198. {
  199. images = ImageUtils.RenderTextFileToImages(Encoding.UTF8.GetString(document.Data));
  200. }
  201. bitmapImages.AddRange(images.Select(x =>
  202. {
  203. try
  204. {
  205. return ImageUtils.LoadImage(x);
  206. }
  207. catch (Exception e)
  208. {
  209. Dispatcher.BeginInvoke(() =>
  210. {
  211. MessageWindow.ShowError($"Cannot load document '{document.FileName}", e);
  212. });
  213. }
  214. return null;
  215. }).Where(x => x != null).Cast<ImageSource>());
  216. foreach (var image in bitmapImages)
  217. {
  218. if (!bitmaps.TryGetValue(document.ID, out var list))
  219. {
  220. list = new List<ImageSource>();
  221. bitmaps[document.ID] = list;
  222. }
  223. list.Add(image);
  224. }
  225. }
  226. ViewDocuments.Clear();
  227. foreach (var scan in SelectedScans)
  228. {
  229. if (bitmaps.TryGetValue(scan.Document.ID, out var list))
  230. {
  231. foreach (var bitmap in list)
  232. {
  233. ViewDocuments.Add(new(bitmap, scan));
  234. }
  235. }
  236. }
  237. lock (_viewListLock)
  238. {
  239. ViewList.Clear();
  240. foreach(var (image, _) in ViewDocuments)
  241. {
  242. ViewList.Add(image);
  243. }
  244. }
  245. }
  246. #endregion
  247. #region Uploading
  248. private static byte[] RenderData(ref string filename, byte[] data)
  249. {
  250. var extension = Path.GetExtension(filename).ToLower();
  251. if ((extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp") && ImageUtils.TryGetImageType(data, out var format))
  252. {
  253. return data;
  254. }
  255. else if (extension == ".pdf")
  256. {
  257. return data;
  258. }
  259. else
  260. {
  261. using var stream = new MemoryStream(data);
  262. filename = Path.ChangeExtension(filename, "pdf");
  263. return DataEntryReGroupWindow.RenderToPDF(filename, stream).SaveToBytes();
  264. }
  265. throw new Exception("Could not render file to PDF");
  266. }
  267. private void DynamicTabItem_Drop(object sender, DragEventArgs e)
  268. {
  269. Task.Run(() =>
  270. {
  271. Dispatcher.Invoke(() =>
  272. {
  273. Progress.Show("Uploading documents");
  274. try
  275. {
  276. var result = DocumentUtils.HandleFileDrop(e);
  277. if (result is not null)
  278. {
  279. foreach (var (filename, stream) in result)
  280. {
  281. var newFilename = filename;
  282. byte[] data;
  283. if (stream is null)
  284. {
  285. data = File.ReadAllBytes(newFilename);
  286. }
  287. else
  288. {
  289. using var memStream = new MemoryStream();
  290. stream.CopyTo(memStream);
  291. data = memStream.ToArray();
  292. }
  293. data = RenderData(ref newFilename, data);
  294. _dataEntryGrid.UploadDocument(newFilename, data, Guid.Empty);
  295. }
  296. }
  297. Progress.Close();
  298. }
  299. catch (Exception e)
  300. {
  301. Progress.Close();
  302. MessageWindow.ShowError("Could not upload documents.", e);
  303. }
  304. });
  305. });
  306. }
  307. private void DynamicTabItem_DragOver(object sender, DragEventArgs e)
  308. {
  309. if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent("FileGroupDescriptor"))
  310. {
  311. e.Effects = DragDropEffects.Copy;
  312. }
  313. else
  314. {
  315. e.Effects = DragDropEffects.None;
  316. }
  317. e.Handled = true;
  318. }
  319. #endregion
  320. private void _documents_OnSelectItem(object sender, DynamicGridSelectionEventArgs e)
  321. {
  322. UpdateViewList(false);
  323. DoSelect(e.Rows);
  324. }
  325. private void DoSelect(CoreRow[] rows)
  326. {
  327. var appliesTo = rows?.Length == 1
  328. ? rows[0].Get<DataEntryDocument, string>(x => x.Tag.AppliesTo)
  329. : "";
  330. var entityid = rows?.Length == 1
  331. ? rows[0].Get<DataEntryDocument, Guid>(x => x.EntityID)
  332. : Guid.Empty;
  333. var archived = rows?.Length == 1
  334. ? rows[0].Get<DataEntryDocument, DateTime>(x => x.Archived)
  335. : DateTime.MinValue;
  336. SelectionChanged?.Invoke(appliesTo, entityid, archived.IsEmpty());
  337. CloseImageWindows();
  338. }
  339. private void _historyGrid_OnOnSelectItem(object sender, DynamicGridSelectionEventArgs e)
  340. {
  341. DoSelect(e.Rows);
  342. }
  343. private void CloseImageWindows()
  344. {
  345. while (OpenWindows.Count > 0)
  346. {
  347. var win = OpenWindows.Last();
  348. OpenWindows.RemoveAt(OpenWindows.Count - 1);
  349. win.Close();
  350. }
  351. }
  352. private void OpenImageWindow(ImageSource image)
  353. {
  354. var window = OpenWindows.FirstOrDefault(x => x.Images.Contains(image));
  355. if (window is not null)
  356. {
  357. window.Activate();
  358. }
  359. else
  360. {
  361. window = new DataEntryDocumentWindow();
  362. window.Topmost = true;
  363. window.Images.Add(image);
  364. OpenWindows.Add(window);
  365. window.Closed += OpenWindow_Closed;
  366. window.Show();
  367. }
  368. }
  369. private void Image_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
  370. {
  371. if (sender is not Image image) return;
  372. if(e.ClickCount >= 2)
  373. {
  374. OpenImageWindow(image.Source);
  375. e.Handled = true;
  376. }
  377. }
  378. private void OpenWindow_Closed(object? sender, EventArgs e)
  379. {
  380. if (sender is not DataEntryDocumentWindow window) return;
  381. OpenWindows.Remove(window);
  382. }
  383. private void _Explode_OnClick(object sender, RoutedEventArgs e)
  384. {
  385. _dataEntryGrid.DoExplode();
  386. }
  387. private List<DataEntryTag>? _tags;
  388. private void _uploadMenu_OnOpened(object sender, RoutedEventArgs e)
  389. {
  390. if (Clipboard.ContainsText())
  391. {
  392. _pasteItem.Header = "Paste Text from Clipboard";
  393. _pasteItem.Visibility = Visibility.Visible;
  394. }
  395. else if (Clipboard.ContainsImage())
  396. {
  397. _pasteItem.Header = "Paste Image from Clipboard";
  398. _pasteItem.Visibility = Visibility.Visible;
  399. }
  400. else if (Clipboard.ContainsFileDropList())
  401. {
  402. int count = CheckAllowableFiles();
  403. if (count > 0)
  404. {
  405. _pasteItem.Header = $@"Paste {count} File{(count > 1 ? "s" : "")} from Clipboard";
  406. _pasteItem.Visibility = Visibility.Visible;
  407. }
  408. }
  409. else
  410. _pasteItem.Visibility = Visibility.Collapsed;
  411. _archive.Visibility = _dataEntryGrid.SelectedRows.Any() && Security.CanEdit<DataEntryDocument>()
  412. ? Visibility.Visible
  413. : Visibility.Collapsed;
  414. _archiveseparator.Visibility = _archive.Visibility;
  415. _tags ??= DataEntryGrid.GetVisibleTagList();
  416. _changeTag.Items.Clear();
  417. foreach (var tag in _tags)
  418. _changeTag.Items.Add(new MenuItem()
  419. {
  420. Header = tag.Name,
  421. Command = new Command((_) => ChangeTag(tag)) { }
  422. });
  423. _changeTag.Items.Add(new Separator());
  424. _changeTag.Items.Add(new MenuItem()
  425. {
  426. Header= "Clear Tags",
  427. Command = new Command((_) => ChangeTag(new DataEntryTag()))
  428. });
  429. _changeTag.Visibility = _dataEntryGrid.SelectedRows.Any() && _tags.Any() && (Security.CanEdit<DataEntryDocument>() || Security.IsAllowed<CanSetupDataEntryTags>())
  430. ? Visibility.Visible
  431. : Visibility.Collapsed;
  432. _changeNote.Visibility = _dataEntryGrid.SelectedRows.Any() && Security.CanEdit<DataEntryDocument>()
  433. ? Visibility.Visible
  434. : Visibility.Collapsed;
  435. _changetagseparator.Visibility = _archive.Visibility;
  436. }
  437. private void ChangeTag(object obj)
  438. {
  439. if (obj is DataEntryTag tag)
  440. _dataEntryGrid.DoChangeTags(tag.ID);
  441. }
  442. private void _addItem_OnClick(object sender, RoutedEventArgs e)
  443. {
  444. var ofd = new OpenFileDialog()
  445. {
  446. Filter = @"All Files (*.pdf, *.bmp, *.png, *.jpg, *.jpeg)|*.pdf;*.bmp;*.png;*.jpg;*.jpeg",
  447. Multiselect = true,
  448. Title = @"Select Files to Upload",
  449. InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
  450. };
  451. if (ofd.ShowDialog() == true)
  452. {
  453. foreach (var file in ofd.FileNames)
  454. Upload(
  455. Path.GetFileName(file),
  456. new FileStream(file,FileMode.Open));
  457. }
  458. }
  459. private void _pasteItem_OnClick(object sender, RoutedEventArgs e)
  460. {
  461. if (Clipboard.ContainsText())
  462. {
  463. Upload(
  464. $"Pasted Text {DateTime.Now:yyyy-MM-dd hh-mm-ss-fff}.txt",
  465. new MemoryStream(new UTF8Encoding().GetBytes(Clipboard.GetText()))
  466. );
  467. }
  468. else if (Clipboard.ContainsImage())
  469. {
  470. var img = Clipboard.GetImage();
  471. if (img != null)
  472. {
  473. var bmp = ImageUtils.BitmapSourceToBitmap(img);
  474. using (var ms = new MemoryStream())
  475. {
  476. bmp.Save(ms, ImageFormat.Png);
  477. Upload(
  478. $"Pasted Image {DateTime.Now:yyyy-MM-dd hh-mm-ss-fff}.png",
  479. ms
  480. );
  481. }
  482. }
  483. }
  484. else if (CheckAllowableFiles() > 0)
  485. {
  486. var files = Clipboard.GetFileDropList().OfType<String>().ToArray();
  487. foreach (var file in files)
  488. {
  489. Upload(
  490. Path.GetFileName(file),
  491. new FileStream(file,FileMode.Open)
  492. );
  493. }
  494. }
  495. }
  496. private void Upload(string filename, Stream data)
  497. {
  498. var doc = DataEntryReGroupWindow.RenderToPDF(filename, data);
  499. _dataEntryGrid.UploadDocument(Path.ChangeExtension(filename,"pdf"), doc.SaveToBytes(), Guid.Empty);
  500. }
  501. private static int CheckAllowableFiles()
  502. {
  503. var extensions = Clipboard.GetFileDropList().OfType<String>().Select(x => Path.GetExtension(x.ToUpper())).ToArray();
  504. return extensions.Count(x =>
  505. String.Equals(x, "PDF")
  506. || String.Equals(x, "PNG")
  507. || String.Equals(x, "JPG")
  508. || String.Equals(x, "JPEG")
  509. || String.Equals(x, "BMP")
  510. || String.Equals(x, "TXT")
  511. );
  512. }
  513. private void _pages_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  514. {
  515. if (_pages.SelectedIndex == 0)
  516. _dataEntryGrid.Refresh(false,true);
  517. else if (_pages.SelectedIndex == 1)
  518. _historyGrid.Refresh(false,true);
  519. }
  520. private void _remove_OnClick(object sender, RoutedEventArgs e)
  521. {
  522. _dataEntryGrid.DoRemove();
  523. }
  524. private void _reopen_OnClick(object sender, RoutedEventArgs e)
  525. {
  526. _historyGrid.DoReopen();
  527. }
  528. private void _changeNote_Click(object sender, RoutedEventArgs e)
  529. {
  530. var notes = _dataEntryGrid.SelectedRows.ToObjects<DataEntryDocument>().Select(x => x.Note).Distinct().ToArray();
  531. var note = notes.Length == 1 ? notes[0] : "";
  532. if(TextBoxDialog.Execute("Enter note:", ref note))
  533. {
  534. _dataEntryGrid.DoChangeNote(note);
  535. }
  536. }
  537. private void _dataEntryGrid_OnContextMenuOpening(object sender, ContextMenuEventArgs e)
  538. {
  539. }
  540. private void _ShowImage_Click(object sender, RoutedEventArgs e)
  541. {
  542. if (sender is not MenuItem item || item.Tag is not ImageSource image) return;
  543. OpenImageWindow(image);
  544. }
  545. private void RotateDocument(Document doc)
  546. {
  547. var extension = Path.GetExtension(doc.FileName).ToLower();
  548. if (extension == ".pdf")
  549. {
  550. var loadeddoc = new PdfLoadedDocument(doc.Data);
  551. foreach (var page in loadeddoc.GetPages())
  552. {
  553. var rotation = (int)page.Rotation;
  554. rotation = (rotation + 1) % 4;
  555. page.Rotation = (PdfPageRotateAngle)rotation;
  556. }
  557. doc.Data = loadeddoc.SaveToBytes();
  558. }
  559. else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp")
  560. {
  561. using var stream = new MemoryStream(doc.Data);
  562. var bitmap = Bitmap.FromStream(stream);
  563. bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
  564. using var outStream = new MemoryStream();
  565. bitmap.Save(outStream, extension switch
  566. {
  567. ".jpg" or ".jpeg" => ImageFormat.Jpeg,
  568. ".png" => ImageFormat.Png,
  569. _ => ImageFormat.Bmp
  570. });
  571. doc.Data = outStream.ToArray();
  572. }
  573. else
  574. {
  575. using var stream = new MemoryStream(doc.Data);
  576. var loadeddoc = DataEntryReGroupWindow.RenderToPDF(doc.FileName, stream);
  577. foreach (var page in loadeddoc.GetPages())
  578. {
  579. var rotation = (int)page.Rotation;
  580. rotation = (rotation + 1) % 4;
  581. page.Rotation = (PdfPageRotateAngle)rotation;
  582. }
  583. doc.Data = loadeddoc.SaveToBytes();
  584. }
  585. }
  586. private void _RotateImage_Click(object sender, RoutedEventArgs e)
  587. {
  588. if (sender is not MenuItem item || item.Tag is not ImageSource image) return;
  589. var document = ViewDocuments.FirstOrDefault(x => x.Item1 == image)?.Item2;
  590. if (document is null)
  591. {
  592. MessageWindow.ShowError("An error occurred", "Document does not exist in ViewDocuments list");
  593. return;
  594. }
  595. var doc = DataEntryCache.Cache.LoadDocuments(CoreUtils.One(document.Document.ID), checkTimestamp: true).First();
  596. try
  597. {
  598. RotateDocument(doc);
  599. }
  600. catch(Exception err)
  601. {
  602. MessageWindow.ShowError("Something went wrong while trying to rotate this document.", err);
  603. return;
  604. }
  605. Client.Save(doc, "Rotated by user.");
  606. if (Path.GetExtension(doc.FileName) == ".pdf")
  607. {
  608. document.Thumbnail = ImageUtils.GetPDFThumbnail(doc.Data, 256, 256);
  609. }
  610. new Client<DataEntryDocument>().Save(document, "");
  611. DataEntryCache.Cache.Add(new DataEntryCachedDocument(doc));
  612. UpdateViewList(true);
  613. }
  614. }