DataEntryReGroupWindow.xaml.cs 13 KB

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