DataEntryReGroupWindow.xaml.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. using Comal.Classes;
  2. using InABox.Core;
  3. using InABox.Wpf;
  4. using InABox.WPF;
  5. using Motorola.Snapi.Attributes;
  6. using PRSDesktop.Panels.DataEntry;
  7. using Syncfusion.Pdf;
  8. using Syncfusion.Pdf.Graphics;
  9. using Syncfusion.Pdf.Parsing;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Collections.ObjectModel;
  13. using System.ComponentModel;
  14. using System.Diagnostics.CodeAnalysis;
  15. using System.Drawing;
  16. using System.IO;
  17. using System.Linq;
  18. using System.Runtime.CompilerServices;
  19. using System.Runtime.InteropServices;
  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.Documents;
  26. using System.Windows.Input;
  27. using System.Windows.Media;
  28. using System.Windows.Media.Imaging;
  29. using DocumentPage = PRSDesktop.Panels.DataEntry.DocumentPage;
  30. using Exception = System.Exception;
  31. using Image = System.Windows.Controls.Image;
  32. namespace PRSDesktop
  33. {
  34. namespace Panels.DataEntry
  35. {
  36. public class DocumentGroup
  37. {
  38. public string FileName { get; set; }
  39. public List<DataEntryReGroupWindow.Page> Pages { get; set; }
  40. public Guid TagID { get; set; }
  41. public DocumentGroup(string fileName, List<DataEntryReGroupWindow.Page> pages, Guid tagID)
  42. {
  43. FileName = fileName;
  44. Pages = pages;
  45. TagID = tagID;
  46. }
  47. }
  48. }
  49. /// <summary>
  50. /// Interaction logic for DocumentManipulationWindow.xaml
  51. /// </summary>
  52. public partial class DataEntryReGroupWindow : Window
  53. {
  54. public List<Page> Pages { get; set; }
  55. public ObservableCollection<DocumentGroup> Groups { get; set; }
  56. private DataEntryTag? SelectedTag => TagBox.SelectedItem as DataEntryTag;
  57. public class Page
  58. {
  59. public string FileName { get; set; }
  60. public PdfLoadedDocument Pdf { get; set; }
  61. public int PageIndex { get; set; }
  62. public BitmapImage? Thumbnail { get; set; }
  63. public Page(string fileName, PdfLoadedDocument pdf, int pageIndex)
  64. {
  65. FileName = fileName;
  66. Pdf = pdf;
  67. PageIndex = pageIndex;
  68. }
  69. }
  70. public DataEntryReGroupWindow(List<Page> pages, string filename, Guid tagID)
  71. {
  72. InitializeComponent();
  73. Groups = new();
  74. GroupName.Text = Path.ChangeExtension(filename, ".pdf");
  75. Pages = pages;
  76. ReloadPages();
  77. var tags = DataEntryGrid.GetVisibleTagList();
  78. TagBox.ItemsSource = tags;
  79. if (tagID != Guid.Empty)
  80. {
  81. TagBox.SelectedItem = tags.FirstOrDefault(x => x.ID == tagID);
  82. }
  83. TagBox.DisplayMemberPath = "Name";
  84. }
  85. private void ReloadPages()
  86. {
  87. Documents.Children.Clear();
  88. foreach (var page in Pages)
  89. {
  90. page.Thumbnail ??= page.Pdf.AsLoadedDocument().ExportAsImage(page.PageIndex).AsBitmapImage(false);
  91. var width = page.Thumbnail.Width;
  92. var height = page.Thumbnail.Height;
  93. var aspectRatio = width / height;
  94. if(height > 300)
  95. {
  96. height = 300;
  97. width = aspectRatio * height;
  98. }
  99. if(width > 300)
  100. {
  101. width = 300;
  102. height = width / aspectRatio;
  103. }
  104. var docPage = new DocumentPage(page) { Height = height, Margin = new Thickness(5) };
  105. docPage.OnSelected += DocPage_OnSelected;
  106. Documents.Children.Add(docPage);
  107. }
  108. }
  109. private void DocPage_OnSelected(DocumentPage page, bool selected)
  110. {
  111. Group.IsEnabled = Documents.Children.Cast<DocumentPage>().Any(x => x.Selected);
  112. }
  113. private void Cancel_Click(object sender, RoutedEventArgs e)
  114. {
  115. DialogResult = false;
  116. }
  117. private void OK_Click(object sender, RoutedEventArgs e)
  118. {
  119. if (Pages.Any())
  120. {
  121. if (string.IsNullOrWhiteSpace(GroupName.Text))
  122. {
  123. MessageBox.Show("You still have ungrouped pages. Please group them into a document and try again.");
  124. return;
  125. }
  126. else
  127. {
  128. var filename = Path.ChangeExtension(GroupName.Text, ".pdf");
  129. if(MessageBox.Show($"You still have ungrouped pages. Do you wish to combine them into a document called \"{filename}\"?", "Combine remaining?", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
  130. {
  131. var group = new DocumentGroup(filename, Pages.ToList(), SelectedTag?.ID ?? Guid.Empty);
  132. Pages.Clear();
  133. Groups.Add(group);
  134. }
  135. else
  136. {
  137. MessageBox.Show("Please group the remaining pages into a document and try again.");
  138. return;
  139. }
  140. }
  141. }
  142. DialogResult = true;
  143. }
  144. #region Grouping
  145. private void Group_Click(object sender, RoutedEventArgs e)
  146. {
  147. var filename = Path.ChangeExtension(GroupName.Text, ".pdf");
  148. if (string.IsNullOrWhiteSpace(filename))
  149. {
  150. MessageBox.Show("Please specify a document name!");
  151. GroupName.Focus();
  152. return;
  153. }
  154. var selected = Documents.Children.Cast<DocumentPage>().Where(x => x.Selected).ToList();
  155. foreach(var page in selected)
  156. {
  157. Documents.Children.Remove(page);
  158. Pages.Remove(page.Page);
  159. }
  160. var group = new DocumentGroup(filename, selected.Select(x => x.Page).ToList(), SelectedTag?.ID ?? Guid.Empty);
  161. Groups.Add(group);
  162. GroupList.ItemsSource = Groups;
  163. GroupName.Text = "";
  164. Group.IsEnabled = false;
  165. }
  166. private void Ungroup_Click(object sender, RoutedEventArgs e)
  167. {
  168. if ((sender as MenuItem)?.Tag is not DocumentGroup group) return;
  169. foreach(var page in group.Pages)
  170. {
  171. Pages.Add(page);
  172. }
  173. Groups.Remove(group);
  174. GroupList.ItemsSource = Groups;
  175. ReloadPages();
  176. }
  177. #endregion
  178. #region Drag & Drop
  179. private static bool TryRenderPDF(Stream stream, [NotNullWhen(true)] out PdfDocumentBase? doc)
  180. {
  181. try
  182. {
  183. doc = new PdfLoadedDocument(stream);
  184. return true;
  185. }
  186. catch (Exception)
  187. {
  188. }
  189. doc = null;
  190. return false;
  191. }
  192. private static bool TryRenderImage(Stream stream, [NotNullWhen(true)] out PdfDocumentBase? doc)
  193. {
  194. try
  195. {
  196. var image = new PdfBitmap(stream);
  197. var pdfDoc = new PdfDocument();
  198. var section = pdfDoc.Sections.Add();
  199. section.PageSettings.Margins.All = 0;
  200. section.PageSettings.Width = image.Width;
  201. section.PageSettings.Height = image.Height;
  202. var page = section.Pages.Add();
  203. page.Graphics.DrawImage(image, 0, 0, page.Size.Width, page.Size.Height);
  204. doc = pdfDoc;
  205. return true;
  206. }
  207. catch(Exception)
  208. {
  209. }
  210. doc = null;
  211. return false;
  212. }
  213. private static bool TryRenderText(Stream stream, [NotNullWhen(true)] out PdfDocumentBase? doc)
  214. {
  215. try
  216. {
  217. var bytes = new byte[stream.Length];
  218. stream.Read(bytes, 0, bytes.Length);
  219. var text = Encoding.UTF8.GetString(bytes);
  220. var pdfDoc = new PdfDocument();
  221. var page = pdfDoc.Pages.Add();
  222. var font = new PdfStandardFont(PdfFontFamily.Courier, 14);
  223. var textElement = new PdfTextElement(text, font);
  224. var layoutFormat = new PdfLayoutFormat
  225. {
  226. Layout = PdfLayoutType.Paginate,
  227. Break = PdfLayoutBreakType.FitPage
  228. };
  229. textElement.Draw(page, new RectangleF(0, 0, page.GetClientSize().Width, page.GetClientSize().Height), layoutFormat);
  230. doc = pdfDoc;
  231. return true;
  232. }
  233. catch (Exception)
  234. {
  235. }
  236. doc = null;
  237. return false;
  238. }
  239. public static PdfDocumentBase RenderToPDF(string filename, Stream stream)
  240. {
  241. var extension = Path.GetExtension(filename).ToLower();
  242. if (extension == ".pdf")
  243. {
  244. if(TryRenderPDF(stream, out var pdf)
  245. || TryRenderImage(stream, out pdf)
  246. || TryRenderText(stream, out pdf))
  247. {
  248. return pdf;
  249. }
  250. }
  251. else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp")
  252. {
  253. if (TryRenderImage(stream, out var pdf)
  254. || TryRenderPDF(stream, out pdf)
  255. || TryRenderText(stream, out pdf))
  256. {
  257. return pdf;
  258. }
  259. }
  260. else
  261. {
  262. if (TryRenderText(stream, out var pdf)
  263. || TryRenderImage(stream, out pdf)
  264. || TryRenderPDF(stream, out pdf))
  265. {
  266. return pdf;
  267. }
  268. }
  269. throw new Exception("Could not render file to PDF");
  270. }
  271. private static PdfDocumentBase RenderToPDF(string filename)
  272. {
  273. using var stream = new FileStream(filename, FileMode.Open);
  274. return RenderToPDF(filename, stream);
  275. }
  276. public static List<Tuple<string, PdfDocumentBase>>? HandleFileDrop(DragEventArgs e)
  277. {
  278. var result = DocumentUtils.HandleFileDrop(e);
  279. if(result is null)
  280. {
  281. return null;
  282. }
  283. var docs = new List<Tuple<string, PdfDocumentBase>>();
  284. foreach (var (filename, stream) in result)
  285. {
  286. if(stream is null)
  287. {
  288. var doc = RenderToPDF(filename);
  289. docs.Add(new(filename, doc));
  290. }
  291. else
  292. {
  293. var doc = RenderToPDF(filename, stream);
  294. docs.Add(new(filename, doc));
  295. }
  296. }
  297. if(docs.Count > 0)
  298. {
  299. return docs;
  300. }
  301. else
  302. {
  303. return null;
  304. }
  305. }
  306. public static List<Page> SplitIntoPages(string filename, PdfDocumentBase doc)
  307. {
  308. var pages = new List<Page>();
  309. var loaded = doc.AsLoadedDocument();
  310. for(int i = 0; i < loaded.PageCount(); ++i)
  311. {
  312. pages.Add(new(filename, loaded, i));
  313. }
  314. return pages;
  315. }
  316. private void Documents_Drop(object sender, DragEventArgs e)
  317. {
  318. Task.Run(() =>
  319. {
  320. var docs = HandleFileDrop(e);
  321. if (docs is not null)
  322. {
  323. Dispatcher.Invoke(() =>
  324. {
  325. var groupName = "";
  326. foreach (var (filename, doc) in docs)
  327. {
  328. groupName = filename;
  329. foreach(var page in SplitIntoPages(filename, doc))
  330. {
  331. Pages.Add(page);
  332. }
  333. }
  334. ReloadPages();
  335. if (string.IsNullOrWhiteSpace(GroupName.Text) && !string.IsNullOrWhiteSpace(groupName))
  336. {
  337. GroupName.Text = Path.ChangeExtension(groupName, ".pdf");
  338. }
  339. });
  340. }
  341. });
  342. }
  343. private void Documents_DragOver(object sender, DragEventArgs e)
  344. {
  345. if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent("FileGroupDescriptor"))
  346. {
  347. e.Effects = DragDropEffects.Copy;
  348. }
  349. else
  350. {
  351. e.Effects = DragDropEffects.None;
  352. }
  353. e.Handled = true;
  354. }
  355. #endregion
  356. }
  357. }