using Comal.Classes; using InABox.Core; using InABox.WPF; using Microsoft.Office.Interop.Outlook; using Motorola.Snapi.Attributes; using PRSDesktop.Panels.DataEntry; using Syncfusion.Pdf; using Syncfusion.Pdf.Graphics; using Syncfusion.Pdf.Parsing; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using DocumentPage = PRSDesktop.Panels.DataEntry.DocumentPage; using Exception = System.Exception; using Image = System.Windows.Controls.Image; namespace PRSDesktop { namespace Panels.DataEntry { public class DocumentGroup { public string FileName { get; set; } public List Pages { get; set; } public Guid TagID { get; set; } public DocumentGroup(string fileName, List pages, Guid tagID) { FileName = fileName; Pages = pages; TagID = tagID; } } } /// /// Interaction logic for DocumentManipulationWindow.xaml /// public partial class DocumentManipulationWindow : Window { public List Pages { get; set; } public ObservableCollection Groups { get; set; } private ScanTag? SelectedTag => TagBox.SelectedItem as ScanTag; public class Page { public string FileName { get; set; } public PdfLoadedDocument Pdf { get; set; } public int PageIndex { get; set; } public BitmapImage? Thumbnail { get; set; } public Page(string fileName, PdfLoadedDocument pdf, int pageIndex) { FileName = fileName; Pdf = pdf; PageIndex = pageIndex; } } public DocumentManipulationWindow(List pages, string filename, Guid tagID) { InitializeComponent(); Groups = new(); GroupName.Text = Path.ChangeExtension(filename, ".pdf"); Pages = pages; ReloadPages(); var tags = ScanGrid.GetVisibleScanTagList(); TagBox.ItemsSource = tags; if (tagID != Guid.Empty) { TagBox.SelectedItem = tags.FirstOrDefault(x => x.ID == tagID); } TagBox.DisplayMemberPath = "Name"; } private void ReloadPages() { Documents.Children.Clear(); foreach (var page in Pages) { page.Thumbnail ??= page.Pdf.AsLoadedDocument().ExportAsImage(page.PageIndex).AsBitmapImage(false); var width = page.Thumbnail.Width; var height = page.Thumbnail.Height; var aspectRatio = width / height; if(height > 300) { height = 300; width = aspectRatio * height; } if(width > 300) { width = 300; height = width / aspectRatio; } var docPage = new DocumentPage(page) { Height = height, Margin = new Thickness(5) }; docPage.OnSelected += DocPage_OnSelected; Documents.Children.Add(docPage); } } private void DocPage_OnSelected(DocumentPage page, bool selected) { Group.IsEnabled = Documents.Children.Cast().Any(x => x.Selected); } private void Cancel_Click(object sender, RoutedEventArgs e) { DialogResult = false; } private void OK_Click(object sender, RoutedEventArgs e) { if (Pages.Any()) { if (string.IsNullOrWhiteSpace(GroupName.Text)) { MessageBox.Show("You still have ungrouped pages. Please group them into a document and try again."); return; } else { var filename = Path.ChangeExtension(GroupName.Text, ".pdf"); 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) { var group = new DocumentGroup(filename, Pages.ToList(), SelectedTag?.ID ?? Guid.Empty); Pages.Clear(); Groups.Add(group); } else { MessageBox.Show("Please group the remaining pages into a document and try again."); return; } } } DialogResult = true; } #region Grouping private void Group_Click(object sender, RoutedEventArgs e) { var filename = Path.ChangeExtension(GroupName.Text, ".pdf"); if (string.IsNullOrWhiteSpace(filename)) { MessageBox.Show("Please specify a document name!"); GroupName.Focus(); return; } var selected = Documents.Children.Cast().Where(x => x.Selected).ToList(); foreach(var page in selected) { Documents.Children.Remove(page); Pages.Remove(page.Page); } var group = new DocumentGroup(filename, selected.Select(x => x.Page).ToList(), SelectedTag?.ID ?? Guid.Empty); Groups.Add(group); GroupList.ItemsSource = Groups; GroupName.Text = ""; Group.IsEnabled = false; } private void Ungroup_Click(object sender, RoutedEventArgs e) { if ((sender as MenuItem)?.Tag is not DocumentGroup group) return; foreach(var page in group.Pages) { Pages.Add(page); } Groups.Remove(group); GroupList.ItemsSource = Groups; ReloadPages(); } #endregion #region Drag & Drop private static bool TryRenderPDF(Stream stream, [NotNullWhen(true)] out PdfDocumentBase? doc) { try { doc = new PdfLoadedDocument(stream); return true; } catch (Exception) { } doc = null; return false; } private static bool TryRenderImage(Stream stream, [NotNullWhen(true)] out PdfDocumentBase? doc) { try { var image = new PdfBitmap(stream); var pdfDoc = new PdfDocument(); var section = pdfDoc.Sections.Add(); section.PageSettings.Margins.All = 0; section.PageSettings.Width = image.Width; section.PageSettings.Height = image.Height; var page = section.Pages.Add(); page.Graphics.DrawImage(image, 0, 0, page.Size.Width, page.Size.Height); doc = pdfDoc; return true; } catch(Exception) { } doc = null; return false; } private static bool TryRenderText(Stream stream, [NotNullWhen(true)] out PdfDocumentBase? doc) { try { var bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); var text = Encoding.UTF8.GetString(bytes); var pdfDoc = new PdfDocument(); var page = pdfDoc.Pages.Add(); var font = new PdfStandardFont(PdfFontFamily.Courier, 14); var textElement = new PdfTextElement(text, font); var layoutFormat = new PdfLayoutFormat { Layout = PdfLayoutType.Paginate, Break = PdfLayoutBreakType.FitPage }; textElement.Draw(page, new RectangleF(0, 0, page.GetClientSize().Width, page.GetClientSize().Height), layoutFormat); doc = pdfDoc; return true; } catch (Exception) { } doc = null; return false; } public static PdfDocumentBase RenderToPDF(string filename, Stream stream) { var extension = Path.GetExtension(filename).ToLower(); if (extension == ".pdf") { if(TryRenderPDF(stream, out var pdf) || TryRenderImage(stream, out pdf) || TryRenderText(stream, out pdf)) { return pdf; } } else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".bmp") { if (TryRenderImage(stream, out var pdf) || TryRenderPDF(stream, out pdf) || TryRenderText(stream, out pdf)) { return pdf; } } else { if (TryRenderText(stream, out var pdf) || TryRenderImage(stream, out pdf) || TryRenderPDF(stream, out pdf)) { return pdf; } } throw new Exception("Could not render file to PDF"); } private static PdfDocumentBase RenderToPDF(string filename) { using var stream = new FileStream(filename, FileMode.Open); return RenderToPDF(filename, stream); } public static List>? HandleFileDrop(DragEventArgs e) { var dataObject = new OutlookDataObject(e.Data); string? desc = null; if (dataObject.GetDataPresent("FileGroupDescriptor")) desc = "FileGroupDescriptor"; else if (dataObject.GetDataPresent("FileGroupDescriptorW")) desc = "FileGroupDescriptorW"; if (desc is not null) { var docs = new List>(); var filenames = (string[])dataObject.GetData(desc); var filestreams = (MemoryStream[])dataObject.GetData("FileContents"); for (var i = 0; i < filenames.Length; i++) { var filename = filenames[i]; var filestream = filestreams[i]; var doc = RenderToPDF(filename, filestream); docs.Add(new(filename, doc)); } return docs; } else if (dataObject.GetDataPresent(DataFormats.FileDrop)) { var docs = new List>(); foreach (var filename in (string[])dataObject.GetData(DataFormats.FileDrop)) { if (File.Exists(filename)) { var doc = RenderToPDF(filename); docs.Add(new(filename, doc)); } } return docs; } return null; } public static List SplitIntoPages(string filename, PdfDocumentBase doc) { var pages = new List(); var loaded = doc.AsLoadedDocument(); for(int i = 0; i < loaded.PageCount(); ++i) { pages.Add(new(filename, loaded, i)); } return pages; } private void Documents_Drop(object sender, DragEventArgs e) { Task.Run(() => { var docs = HandleFileDrop(e); if (docs is not null) { Dispatcher.Invoke(() => { var groupName = ""; foreach (var (filename, doc) in docs) { groupName = filename; foreach(var page in SplitIntoPages(filename, doc)) { Pages.Add(page); } } ReloadPages(); if (string.IsNullOrWhiteSpace(GroupName.Text) && !string.IsNullOrWhiteSpace(groupName)) { GroupName.Text = Path.ChangeExtension(groupName, ".pdf"); } }); } }); } private void Documents_DragOver(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent("FileGroupDescriptor")) { e.Effects = DragDropEffects.Copy; } else { e.Effects = DragDropEffects.None; } e.Handled = true; } #endregion } }